diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..ce7a23b3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: Cargo Build & Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + build_and_test: + name: Rust project - latest + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - nightly + steps: + - uses: actions/checkout@v2 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - name: Cargo build + run: cargo build --verbose --release + - name: Cargo test + run: cargo test --release --verbose + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - run: rustup component add clippy + - run: cargo clippy --all-targets -- -D warnings + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a73036e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/verifier/data/test_circuit +/test-vectors/mips +push.sh diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..bab06ca5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "contracts/lib/forge-std"] + path = contracts/lib/forge-std + url = https://github.com/foundry-rs/forge-std.git diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..d8194f96 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + + "host-program" + ] +resolver = "2" \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cedfb451 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CARGO = cargo + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + CARGO += --config 'build.rustdocflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]' +endif + +help: ## Display this help screen + @grep -h \ + -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ + awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +clippy: ## Run clippy checks over all workspace members and formatted correctly + @cargo check + @cargo fmt --all -- --check + @cargo clippy --all-targets -- -D warnings + +fix: ## Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets` + @cargo clippy --fix + +test: ## Run tests for all the workspace members + @cargo test --release --all + +.PHONY: clippy fmt test \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..1070f307 --- /dev/null +++ b/README.md @@ -0,0 +1,192 @@ +# ZKM Project Template + +This is a template for creating an end-to-end ZKM project which can generate the EVM-Compatible proof and the on chain verification contract. + +There are two ways to prove the guest program: +* Use your local machine +* Use ZKM Proving network + +## Local Proving + +### Requirements +* Hardware: X86 CPU, 32 cores, 32G memory + +* OS: Ubuntu22 + +* Rust: 1.81.0-nightly + +* Go : 1.22.1 + +### Running the project + +#### 0. Build guest program ELF + +Please refer to : guest-program/README.md + +#### 1. Generate plonky2 proof + +> [!NOTE] +> If the program excutes succussfully, it will generate three files in the directory verifier/data/test_circuit/.(common_circuit_data.json proof_with_public_inputs.json verifier_only_circuit_data.json) + +* Go program + +``` +mkdir -p /tmp/zkm +git clone https://github.com/zkMIPS/zkm-project-template.git +cd zkm-project-template +RUST_LOG=info SEG_OUTPUT=/tmp/zkm SEG_SIZE=262144 cargo run --release --bin add-go-prove +``` + +If the memory is insufficient, please reduce the SEG_SIZE to 131072 . + +* Rust program + +``` +cd zkm-project-template +RUST_LOG=info SEG_OUTPUT=/tmp/zkm SEG_SIZE=262144 cargo run --release --bin revme-prove +``` +If the memory is insufficient, please reduce the SEG_SIZE to 131072 . + + +#### 2. Convert plonky2 proof to groth16 proof + +Copy the three files generated in the previous step to the testdata/mips directory. + +``` +cp verifier/data/test_circuit/* testdata/mips +./benchmark +``` + +If benchmark excutes succussfully, it will generate a groth16 proof and verifying.key in the directory testdata/mips/. +Then, copying the proof file and verifying.key to contracts/verifier + +``` +cp testdata/mips/snark_proof_with_public_inputs.json contracts/verifier/ +cp testdata/mips/verifying.key contracts/verifier/ +``` + +#### 3. Generate the on chain verification contract. + +``` +./gnark_sol_caller generate --outputDir contracts/src +``` + +#### 4. Deploy Verifier Contract. + +If your system does not has Foundry,please install it. + +``` +curl -L https://foundry.paradigm.xyz | bash +``` + +Then, deploy the contract refering to "### Deploy" in the contracts/README.md . + +## Network Proving + +> [!NOTE] +> The proving network is demo at present. The production version is coming soon. + +### Requirements +* CA certificate: ca.pem, ca.key +* Register in the https://www.zkm.io/apply (Let your key be in the whitelist) + +### Running the project + +#### 0. Build guest program ELF + +Please refer to : guest-program/README.md + +#### 1. Download the block + +The block has your transaction. +We use the following tool to download the block. + +* Compile the tool. + +``` + git clone https://github.com/zkMIPS/cannon-mips + cd cannon-mips && make +``` + +* Config the tool. + +``` + mkdir -p /tmp/cannon + export BASEDIR=/tmp/cannon; + export NODE=https://eth-sepolia.g.alchemy.com/v2/RH793ZL_pQkZb7KttcWcTlOjPrN0BjOW +``` + +* Download some block. + +``` + minigeth/go-ethereum 13284491 +``` +If it executes successfully, you will see the block information in the directory /tmp/cannon/0_13284491 . + +#### 2. Config your CA certificate + +Put the ca.key and ca.pem to some directory , such as , host-program/tool/ . + +If you don't have a CA certificate, you can generate it using the certgen.sh in the director zkm-project-template/host-program/tool/. +``` + cd zkm-project-template/host-program/tool/ + chmod +x certgen.sh + ./certgen.sh +``` + +#### 3. Generate the groth16 proof and verifier Contract + +* Set the Environment parameters. + +``` +cd zkm-project-template +export CA_CERT_PATH=host-program/tool/ca.pem +export PRIVATE_KEY=df4bc5647fdb9600ceb4943d4adff3749956a8512e5707716357b13d5ee687d9 ##For testing, No changing the key! + +export RUST_LOG=info +export ENDPOINT=https://152.32.186.45:20002 ##the test entry of zkm proving network +export SEG_SIZE=131072 +export BLOCK_PATH=/tmp/cannon/0_13284491 +export OUTPUT_DIR=/tmp/zkm +export ELF_PATH=guest-program/mips-elf/zkm-mips-elf-revme-rust +``` + +* Run the host program. + +``` +ARGS='12345678 654321' cargo run --release --bin revme-network-prove +``` + +If it executes successfully, it will output the similar message: +``` +[2024-08-28T03:20:55Z INFO stage] request: "1509d5b6-a9e3-4b2f-85b8-5739c35a1310" +[2024-08-28T03:20:58Z INFO stage] generate_proof response: GenerateProofResponse { status: 2, error_message: "", proof_id: "1509d5b6-a9e3-4b2f-85b8-5739c35a1310", proof_url: "http://152.32.186.45:20001/1509d5b6-a9e3-4b2f-85b8-5739c35a1310/final/proof_with_public_inputs.json", stark_proof_url: "http://152.32.186.45:20001/1509d5b6-a9e3-4b2f-85b8-5739c35a1310/aggregate/proof_with_public_inputs.json", solidity_verifier_url: "http://152.32.186.45:20001/verifier.sol", output_stream: [] } +[2024-08-28T03:21:52Z INFO stage] generate_proof success public_inputs_size: 1546, output_size: 0 +[2024-08-28T03:21:52Z INFO stage] Elapsed time: 56 secs +``` + +* Download the proof and contract + +In the above output, we need the proof_url: "http://152.32.186.45:20001/1509d5b6-a9e3-4b2f-85b8-5739c35a1310/final/proof_with_public_inputs.json" and solidity_verifier_url: "http://152.32.186.45:20001/verifier.sol" . + +``` +wget http://152.32.186.45:20001/1509d5b6-a9e3-4b2f-85b8-5739c35a1310/final/proof_with_public_inputs.json +wget http://152.32.186.45:20001/verifier.sol +``` +Then, move the proof and verifier.sol to contracts directory. +``` +mv proof_with_public_inputs.json contracts/verifier/ +mv verifier.sol contracts/src/ +``` + +#### 4. Deploy Verifier Contract. + +If your system does not has Foundry,please install it. + +``` +curl -L https://foundry.paradigm.xyz | bash +``` + +Then, deploy the contract refering to contracts/README.md + + diff --git a/benchmark b/benchmark new file mode 100755 index 00000000..15ed0b8b Binary files /dev/null and b/benchmark differ diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..61ceaef5 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +large-error-threshold = 1000 \ No newline at end of file diff --git a/contracts/.gitignore b/contracts/.gitignore new file mode 100644 index 00000000..d710e2e2 --- /dev/null +++ b/contracts/.gitignore @@ -0,0 +1,15 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +/broadcast +/broadcast/*/11155111/ +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env \ No newline at end of file diff --git a/contracts/README.md b/contracts/README.md new file mode 100644 index 00000000..3aba4332 --- /dev/null +++ b/contracts/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test (TBD) +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/erc20.s.sol:ERC20TokenScript --rpc-url https://eth-sepolia.g.alchemy.com/v2/RH793ZL_pQkZb7KttcWcTlOjPrN0BjOW --private-key df4bc5647fdb9600ceb4943d4adff3749956a8512e5707716357b13d5ee687d9 +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/contracts/foundry.toml b/contracts/foundry.toml new file mode 100644 index 00000000..9430db16 --- /dev/null +++ b/contracts/foundry.toml @@ -0,0 +1,7 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +fs_permissions = [{ access = "read-write", path = "./" }] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std new file mode 160000 index 00000000..4695fac4 --- /dev/null +++ b/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 4695fac44b2934aaa6d7150e2eaf0256fdc566a7 diff --git a/contracts/remappings.txt b/contracts/remappings.txt new file mode 100644 index 00000000..e69de29b diff --git a/contracts/script/erc20.s.sol b/contracts/script/erc20.s.sol new file mode 100644 index 00000000..4735050f --- /dev/null +++ b/contracts/script/erc20.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {ERC20Token} from "../src/erc20.sol"; + +contract ERC20TokenScript is Script { + ERC20Token public erc20; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + erc20 = new ERC20Token(); + + vm.stopBroadcast(); + } +} diff --git a/contracts/src/erc20.sol b/contracts/src/erc20.sol new file mode 100644 index 00000000..e9bf4c62 --- /dev/null +++ b/contracts/src/erc20.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.0; + +import "./verifier.sol"; + +interface IERC20 { + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +contract ERC20Token is IERC20 { + string private _name; + string private _symbol; + uint8 private _decimals; + uint256 private _totalSupply; + Verifier private _verifier; + + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) allowed; + + constructor() { + _name = "My Token"; + _symbol = "MTK"; + _decimals = 18; + _totalSupply = 0; + _verifier = new Verifier(); + } + + function mintWithProof( + address to, + uint256 value, + Verifier.Proof memory proof, + uint256[65] memory input, + uint256[2] memory proof_commitment + ) public { + require(_verifier.verifyTx(proof, input, proof_commitment), "Proof is invalid"); + _totalSupply += value; + balances[to] += value; + } + + function name() public view returns (string memory) { + return _name; + } + + function symbol() public view returns (string memory) { + return _symbol; + } + + function decimals() public view returns (uint8) { + return _decimals; + } + + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + function balanceOf(address tokenOwner) public view override returns (uint256 balance) { + return balances[tokenOwner]; + } + + function allowance(address tokenOwner, address spender) public view override returns (uint256 remaining) { + return allowed[tokenOwner][spender]; + } + + function approve(address spender, uint256 tokens) public override returns (bool success) { + require(spender != address(0)); + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + return true; + } + + function transfer(address to, uint256 tokens) public override returns (bool success) { + require(to != address(0)); + balances[msg.sender] -= tokens; + balances[to] += tokens; + emit Transfer(msg.sender, to, tokens); + return true; + } + + function transferFrom(address from, address to, uint256 tokens) public override returns (bool success) { + balances[from] -= tokens; + allowed[from][msg.sender] -= tokens; + balances[to] += tokens; + emit Transfer(from, to, tokens); + return true; + } +} diff --git a/contracts/src/verifier.sol b/contracts/src/verifier.sol new file mode 100644 index 00000000..93c079bb --- /dev/null +++ b/contracts/src/verifier.sol @@ -0,0 +1,355 @@ +// This file is MIT Licensed. +// +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +pragma solidity ^0.8.0; + +library Pairing { + struct G1Point { + uint256 X; + uint256 Y; + } + // Encoding of field elements is: X[0] * z + X[1] + + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + /// @return the generator of G1 + + function P1() internal pure returns (G1Point memory) { + return G1Point(1, 2); + } + /// @return the generator of G2 + + function P2() internal pure returns (G2Point memory) { + return G2Point( + [ + 10857046999023057135944570762232829481370756359578518086990519993285655852781, + 11559732032986387107991004021392285783925812861821192530917403151452391805634 + ], + [ + 8495653923123431417604973247489272438418190587263600148770280649306958101930, + 4082367875863433681332203403145435568316851327593401208105741076214120093531 + ] + ); + } + /// @return the negation of p, i.e. p.addition(p.negate()) should be zero. + + function negate(G1Point memory p) internal pure returns (G1Point memory) { + // The prime q in the base field F_q for G1 + uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } + return G1Point(p.X, q - (p.Y % q)); + } + /// @return r the sum of two points of G1 + + function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + require(success); + } + + /// @return r the product of a point on G1 and a scalar, i.e. + /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p. + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success); + } + /// @return the result of computing the pairing check + /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should + /// return true. + + function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { + require(p1.length == p2.length); + uint256 elements = p1.length; + uint256 inputSize = elements * 6; + uint256[] memory input = new uint256[](inputSize); + for (uint256 i = 0; i < elements; i++) { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[1]; + input[i * 6 + 3] = p2[i].X[0]; + input[i * 6 + 4] = p2[i].Y[1]; + input[i * 6 + 5] = p2[i].Y[0]; + } + uint256[1] memory out; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + // switch success case 0 { invalid() } + } + + require(success, "no"); + return out[0] != 0; + } + /// Convenience method for a pairing check for two pairs. + + function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) + internal + view + returns (bool) + { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for three pairs. + + function pairingProd3( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](3); + G2Point[] memory p2 = new G2Point[](3); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for four pairs. + + function pairingProd4( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](4); + G2Point[] memory p2 = new G2Point[](4); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p1[3] = d1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + p2[3] = d2; + return pairing(p1, p2); + } +} + +contract Verifier { + event VerifyEvent(address user); + event Value(uint256 x, uint256 y); + + using Pairing for *; + + struct VerifyingKey { + Pairing.G1Point alpha; + Pairing.G2Point beta; + Pairing.G2Point gamma; + Pairing.G2Point delta; + Pairing.G1Point[] gamma_abc; + } + + struct Proof { + Pairing.G1Point a; + Pairing.G2Point b; + Pairing.G1Point c; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alpha = Pairing.G1Point( + uint256(1302117677361910542468231459920151979091943974637170989682078414965483188281), + uint256(3867027232541254772075321782990110912188042242600509887048224814657257159698) + ); + vk.beta = Pairing.G2Point( + [ + uint256(14801598391870170737198692701386770912604845156083007898115207806880331104566), + uint256(8887197963407115464952338401821009369268043467003949766013809662131496593072) + ], + [ + uint256(2302950197816344635982649983475492746659754717141582763685467658478763816306), + uint256(14546187023230700546432589234473482335485386237848823348478034929543161739283) + ] + ); + vk.gamma = Pairing.G2Point( + [ + uint256(21193141255951568057241429222082940283695501709413235369564469208569004052793), + uint256(3543300415735709873986269632293216615803290229738110628079419132119895168426) + ], + [ + uint256(19424433138581986634666612545039047288274363442950180138503536129758457024677), + uint256(6362422927587009743413699214616594497342377797306122883504232193179957350849) + ] + ); + vk.delta = Pairing.G2Point( + [ + uint256(7385068669210247546745717886468250961916074106623897850964828221823027078300), + uint256(19192861161778484819667846292071148227610103974433548788489138394128409122900) + ], + [ + uint256(16629788660241964719751663969097289423751591856184503635027243409639557586033), + uint256(12621057362679150933080455091106081399032880392542297635602848266829657178901) + ] + ); + vk.gamma_abc = new Pairing.G1Point[](66); + vk.gamma_abc[0] = Pairing.G1Point( + uint256(8227080285934790069042597020674361445558585705178995755056818730155727831239), + uint256(10964499248881051681906280027475314624544858740993358611805036213651538481961) + ); + vk.gamma_abc[1] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[2] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[3] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[4] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[5] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[6] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[7] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[8] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[9] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[10] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[11] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[12] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[13] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[14] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[15] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[16] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[17] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[18] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[19] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[20] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[21] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[22] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[23] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[24] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[25] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[26] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[27] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[28] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[29] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[30] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[31] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[32] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[33] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[34] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[35] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[36] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[37] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[38] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[39] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[40] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[41] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[42] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[43] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[44] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[45] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[46] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[47] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[48] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[49] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[50] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[51] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[52] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[53] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[54] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[55] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[56] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[57] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[58] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[59] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[60] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[61] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[62] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[63] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[64] = Pairing.G1Point(uint256(0), uint256(0)); + vk.gamma_abc[65] = Pairing.G1Point( + uint256(4811611970921445049598943815770920513454731152861450007955704045904060821183), + uint256(5288069492546863186831394199978690427589784869292363777070175867300583082895) + ); + } + + function verify(uint256[65] memory input, Proof memory proof, uint256[2] memory proof_commitment) + public + view + returns (uint256) + { + uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + VerifyingKey memory vk = verifyingKey(); + require(input.length + 1 == vk.gamma_abc.length); + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint256 i = 0; i < input.length; i++) { + require(input[i] < snark_scalar_field); + vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i])); + } + Pairing.G1Point memory p_c = Pairing.G1Point(proof_commitment[0], proof_commitment[1]); + + vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]); + vk_x = Pairing.addition(vk_x, p_c); + + if ( + !Pairing.pairingProd4( + proof.a, + proof.b, + Pairing.negate(vk_x), + vk.gamma, + Pairing.negate(proof.c), + vk.delta, + Pairing.negate(vk.alpha), + vk.beta + ) + ) { + return 1; + } + + return 0; + } + + function verifyTx(Proof memory proof, uint256[65] memory input, uint256[2] memory proof_commitment) + public + returns (bool r) + { + if (verify(input, proof, proof_commitment) == 0) { + emit VerifyEvent(msg.sender); + return true; + } else { + return false; + } + } +} diff --git a/contracts/test/erc20.t.sol b/contracts/test/erc20.t.sol new file mode 100644 index 00000000..0f42fd92 --- /dev/null +++ b/contracts/test/erc20.t.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {Verifier} from "../src/verifier.sol"; +import {StdUtils} from "forge-std/StdUtils.sol"; +import {console} from "forge-std/console.sol"; + +struct XX { + bytes A0 ; + bytes A1 ; +} + +struct BS { + XX X; + XX Y; +} + +struct AR { + bytes X ; + bytes Y ; +} + +struct Commitment { + bytes X ; + bytes Y ; +} + +struct PROOF { + AR Ar ; + AR Krs; + BS Bs; + Commitment[] Commitments; + Commitment CommitmentPok; +} + +struct ProofPublicData{ + PROOF Proof; + bytes[] PublicWitness; +} + +//////// + +struct PairingG1Point { + uint256 X; + uint256 Y; +} + +struct PairingG2Point { + uint256 [2]X; + uint256 [2]Y; +} + +struct VerifierProof { + PairingG1Point A ; + PairingG2Point B ; + PairingG1Point C ; +} + + +contract VerifierTest is Test { + using stdJson for string; + + Verifier public verifier; + + + function loadProof() public view returns (ProofPublicData memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/verifier/snark_proof_with_public_inputs.json"); + string memory json = vm.readFile(path); + bytes memory jsonBytes = json.parseRaw("."); + return abi.decode(jsonBytes, (ProofPublicData)); + } + + function setUp() public { + ProofPublicData memory proof = loadProof(); + + verifier = new Verifier(); + } + + function test_ValidProof() public { + ProofPublicData memory proof = loadProof(); + uint256 [65] memory input; + for (uint256 i = 0; i < proof.PublicWitness.length; i++ ){ + input[i]= StdUtils.bytesToUint(proof.PublicWitness[i]); + } + + Verifier.Proof memory verifierProof; + + verifierProof.a.X = StdUtils.bytesToUint(proof.Proof.Ar.X); + verifierProof.a.Y = StdUtils.bytesToUint(proof.Proof.Ar.Y); + + verifierProof.b.X[0] = StdUtils.bytesToUint(proof.Proof.Bs.X.A0); + verifierProof.b.X[1] = StdUtils.bytesToUint(proof.Proof.Bs.X.A1); + + verifierProof.b.Y[0] = StdUtils.bytesToUint(proof.Proof.Bs.Y.A0); + verifierProof.b.Y[1] = StdUtils.bytesToUint(proof.Proof.Bs.Y.A1); + + verifierProof.c.X = StdUtils.bytesToUint(proof.Proof.Krs.X); + verifierProof.c.Y = StdUtils.bytesToUint(proof.Proof.Krs.Y); + + uint256 [2] memory proofCommitment; + proofCommitment[0] = StdUtils.bytesToUint(proof.Proof.Commitments[0].X); + proofCommitment[1] = StdUtils.bytesToUint(proof.Proof.Commitments[0].Y); + + bool ret ; + ret = verifier.verifyTx(verifierProof, input, proofCommitment); + + assert(ret == true); + } +} diff --git a/contracts/verifier/snark_proof_with_public_inputs.json b/contracts/verifier/snark_proof_with_public_inputs.json new file mode 100644 index 00000000..cc3f7263 --- /dev/null +++ b/contracts/verifier/snark_proof_with_public_inputs.json @@ -0,0 +1 @@ +{"Proof":{"Ar":{"X":"20986100223742189794428594083244953701408789026843989908435048719917632304610","Y":"13312112978838104297936765092731357265583798633367270419949950449739950262299"},"Krs":{"X":"21414385011717857401350318474624945713895831525422758574143367432063095862610","Y":"16423991045129868936811412810791234200886558293119184537442651275319977235533"},"Bs":{"X":{"A0":"11334377274679574919826609438981352168291239642667203135465250445892409261829","A1":"502135620489275023389952763328439911399553648581965110109386553942302300788"},"Y":{"A0":"17625733828018823879938709981005705770193800659886938923296199657306934864258","A1":"4260326572269494480125515901189030920228567526305403135494458666071296191877"}},"Commitments":[{"X":"17273959889587427045931700521956542100389478452246646316053540421122551664021","Y":"18318673986437913163676530971051070041062128124589353307942427661936033278456"}],"CommitmentPok":{"X":"5949616844048677802101960585122116163044527517837594064636603012726545439747","Y":"4465071354076921449919725370557406791935776422229056588281381686837540526870"}},"PublicWitness":["3","176","196","66","152","252","28","20","154","251","244","200","153","111","185","36","39","174","65","228","100","155","147","76","164","149","153","27","120","82","184","85","3","176","196","66","152","252","28","20","154","251","244","200","153","111","185","36","39","174","65","228","100","155","147","76","164","149","153","27","120","82","184","85","5009287862160307920686587520880862879519265129424853749905862185648451074148"]} \ No newline at end of file diff --git a/contracts/verifier/verifier.sol.tmpl b/contracts/verifier/verifier.sol.tmpl new file mode 100644 index 00000000..9352be37 --- /dev/null +++ b/contracts/verifier/verifier.sol.tmpl @@ -0,0 +1,212 @@ +// This file is MIT Licensed. +// +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +pragma solidity ^0.8.0; +library Pairing { + struct G1Point { + uint X; + uint Y; + } + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint[2] X; + uint[2] Y; + } + /// @return the generator of G1 + function P1() pure internal returns (G1Point memory) { + return G1Point(1, 2); + } + /// @return the generator of G2 + function P2() pure internal returns (G2Point memory) { + return G2Point( + [10857046999023057135944570762232829481370756359578518086990519993285655852781, + 11559732032986387107991004021392285783925812861821192530917403151452391805634], + [8495653923123431417604973247489272438418190587263600148770280649306958101930, + 4082367875863433681332203403145435568316851327593401208105741076214120093531] + ); + } + /// @return the negation of p, i.e. p.addition(p.negate()) should be zero. + function negate(G1Point memory p) pure internal returns (G1Point memory) { + // The prime q in the base field F_q for G1 + uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + if (p.X == 0 && p.Y == 0) + return G1Point(0, 0); + return G1Point(p.X, q - (p.Y % q)); + } + /// @return r the sum of two points of G1 + function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { + uint[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require(success); + } + + + /// @return r the product of a point on G1 and a scalar, i.e. + /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p. + function scalar_mul(G1Point memory p, uint s) internal view returns (G1Point memory r) { + uint[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require (success); + } + /// @return the result of computing the pairing check + /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should + /// return true. + function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { + require(p1.length == p2.length); + uint elements = p1.length; + uint inputSize = elements * 6; + uint[] memory input = new uint[](inputSize); + for (uint i = 0; i < elements; i++) + { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[1]; + input[i * 6 + 3] = p2[i].X[0]; + input[i * 6 + 4] = p2[i].Y[1]; + input[i * 6 + 5] = p2[i].Y[0]; + } + uint[1] memory out; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + // switch success case 0 { invalid() } + } + + require(success,"no"); + return out[0] != 0; + } + /// Convenience method for a pairing check for two pairs. + function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for three pairs. + function pairingProd3( + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](3); + G2Point[] memory p2 = new G2Point[](3); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for four pairs. + function pairingProd4( + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2, + G1Point memory d1, G2Point memory d2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](4); + G2Point[] memory p2 = new G2Point[](4); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p1[3] = d1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + p2[3] = d2; + return pairing(p1, p2); + } +} + +contract Verifier { + event VerifyEvent(address user); + event Value(uint x, uint y); + + using Pairing for *; + struct VerifyingKey { + Pairing.G1Point alpha; + Pairing.G2Point beta; + Pairing.G2Point gamma; + Pairing.G2Point delta; + Pairing.G1Point[] gamma_abc; + } + struct Proof { + Pairing.G1Point a; + Pairing.G2Point b; + Pairing.G1Point c; + } + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = {{.Alpha}}; + vk.beta = {{.Beta}}; + vk.gamma = {{.Gamma}}; + vk.delta = {{.Delta}}; + {{.Gamma_abc}} + } + function verify(uint[65] memory input, Proof memory proof, uint[2] memory proof_commitment) public view returns (uint) { + uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + VerifyingKey memory vk = verifyingKey(); + require(input.length + 1 == vk.gamma_abc.length); + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint i = 0; i < input.length; i++) { + require(input[i] < snark_scalar_field); + vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i])); + } + Pairing.G1Point memory p_c = Pairing.G1Point(proof_commitment[0], proof_commitment[1]); + + vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]); + vk_x = Pairing.addition(vk_x, p_c); + + if(!Pairing.pairingProd4( + proof.a, proof.b, + Pairing.negate(vk_x), vk.gamma, + Pairing.negate(proof.c), vk.delta, + Pairing.negate(vk.alpha), vk.beta)) { + return 1; + } + + return 0; + } + function verifyTx( + Proof memory proof, uint[65] memory input + ,uint[2] memory proof_commitment) public returns (bool r) { + + if (verify(input, proof , proof_commitment) == 0) { + emit VerifyEvent(msg.sender); + return true; + } else { + return false; + } + + } +} diff --git a/contracts/verifier/verifying.key b/contracts/verifier/verifying.key new file mode 100644 index 00000000..1b9ce35d Binary files /dev/null and b/contracts/verifier/verifying.key differ diff --git a/gnark_sol_caller b/gnark_sol_caller new file mode 100755 index 00000000..e0287192 Binary files /dev/null and b/gnark_sol_caller differ diff --git a/guest-program/README.md b/guest-program/README.md new file mode 100644 index 00000000..c00fa7b6 --- /dev/null +++ b/guest-program/README.md @@ -0,0 +1,20 @@ +# Guest Program Examples + +ZKM can generate proof for Go and Rust (guest) Programs. + +> [!NOTE] +> In the mips-elf directory, We have prepared the relative mips ELF which are ready for proof use. + +We will provide users with Go and Rust tools to facilitate the building of guest programs. +But you should use the zkm repo to build the Go/Rust guest programs at present. Please refer to https://github.com/zkMIPS/zkm/blob/main/prover/examples/README.md + +``` +cd prover/examples/add-go +GOOS=linux GOARCH=mips GOMIPS=softfloat go build . +``` +or +``` +cargo build --target=mips-unknown-linux-musl +``` + + diff --git a/guest-program/add-go/add.go b/guest-program/add-go/add.go new file mode 100644 index 00000000..d1f603f8 --- /dev/null +++ b/guest-program/add-go/add.go @@ -0,0 +1,48 @@ +package main + +import "github.com/zkMIPS/zkm/go-runtime/zkm_runtime" + +type DataId uint32 + +// use iota to create enum +const ( + TYPE1 DataId = iota + TYPE2 + TYPE3 +) + +type Data struct { + Input1 [10]byte + Input2 uint8 + Input3 int8 + Input4 uint16 + Input5 int16 + Input6 uint32 + Input7 int32 + Input8 uint64 + Input9 int64 + Input10 []byte + Input11 DataId + Input12 string +} + +func main() { + a := zkm_runtime.Read[Data]() + a.Input1[0] = a.Input1[0] + a.Input1[1] + a.Input2 = a.Input2 + a.Input2 + a.Input3 = a.Input3 + a.Input3 + a.Input4 = a.Input4 + a.Input4 + a.Input5 = a.Input5 + a.Input5 + a.Input6 = a.Input6 + a.Input6 + a.Input7 = a.Input7 + a.Input7 + a.Input8 = a.Input8 + a.Input8 + a.Input9 = a.Input9 + a.Input9 + if a.Input11 != TYPE3 { + println("enum type error") + } + if a.Input12 != "hello" { + println("string type error") + } + a.Input10[0] = a.Input10[0] + a.Input10[1] + zkm_runtime.Commit[Data](a) +} diff --git a/guest-program/add-go/go.mod b/guest-program/add-go/go.mod new file mode 100644 index 00000000..4ca1128f --- /dev/null +++ b/guest-program/add-go/go.mod @@ -0,0 +1,7 @@ +module go-add + +go 1.22.5 + +replace github.com/zkMIPS/zkm/go-runtime/zkm_runtime => ../../../go-runtime/zkm_runtime + +require github.com/zkMIPS/zkm/go-runtime/zkm_runtime v0.0.0-00010101000000-000000000000 diff --git a/guest-program/mips-elf/zkm-mips-elf-add-go b/guest-program/mips-elf/zkm-mips-elf-add-go new file mode 100755 index 00000000..2188f44f Binary files /dev/null and b/guest-program/mips-elf/zkm-mips-elf-add-go differ diff --git a/guest-program/mips-elf/zkm-mips-elf-revme-rust b/guest-program/mips-elf/zkm-mips-elf-revme-rust new file mode 100755 index 00000000..39258ed5 Binary files /dev/null and b/guest-program/mips-elf/zkm-mips-elf-revme-rust differ diff --git a/guest-program/revme/Cargo.lock b/guest-program/revme/Cargo.lock new file mode 100644 index 00000000..b899eb7c --- /dev/null +++ b/guest-program/revme/Cargo.lock @@ -0,0 +1,1004 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "alloy-primitives" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +dependencies = [ + "bytes", +] + +[[package]] +name = "aurora-engine-modexp" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aef7712851e524f35fbbb74fa6599c5cd8692056a1c36f9ca0d2001b670e7e5" +dependencies = [ + "hex", + "num", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] + +[[package]] +name = "cc" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enumn" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "evm" +version = "0.1.0" +dependencies = [ + "ahash", + "k256", + "libc", + "models", + "revm", + "serde", + "serde_json", + "zkm-runtime", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "models" +version = "0.1.0" +source = "git+https://github.com/eigmax/powdr-revme?branch=continuations#0abf75bce415b9f713dcc86a034bf6977b984583" +dependencies = [ + "revm", + "serde", + "serde_json", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bitflags", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "revm" +version = "3.5.0" +source = "git+https://github.com/powdr-labs/revm?branch=serde-no-std#8653c72bfd70cbac061b03d0c0de02153dbc6601" +dependencies = [ + "auto_impl", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "1.3.0" +source = "git+https://github.com/powdr-labs/revm?branch=serde-no-std#8653c72bfd70cbac061b03d0c0de02153dbc6601" +dependencies = [ + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "2.2.0" +source = "git+https://github.com/powdr-labs/revm?branch=serde-no-std#8653c72bfd70cbac061b03d0c0de02153dbc6601" +dependencies = [ + "aurora-engine-modexp", + "k256", + "once_cell", + "revm-primitives", + "ripemd", + "sha2", + "substrate-bn", +] + +[[package]] +name = "revm-primitives" +version = "1.3.0" +source = "git+https://github.com/powdr-labs/revm?branch=serde-no-std#8653c72bfd70cbac061b03d0c0de02153dbc6601" +dependencies = [ + "alloy-primitives", + "auto_impl", + "bitflags", + "bitvec", + "c-kzg", + "enumn", + "hashbrown", + "hex", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zkm-precompiles" +version = "0.1.0" +dependencies = [ + "bincode", + "cfg-if", + "serde", +] + +[[package]] +name = "zkm-runtime" +version = "0.1.0" +dependencies = [ + "bincode", + "cfg-if", + "getrandom", + "lazy_static", + "libm", + "rand", + "serde", + "sha2", + "zkm-precompiles", +] diff --git a/guest-program/revme/Cargo.toml b/guest-program/revme/Cargo.toml new file mode 100644 index 00000000..853e9b8b --- /dev/null +++ b/guest-program/revme/Cargo.toml @@ -0,0 +1,30 @@ +[workspace] +[package] +name = "evm" +version = "0.1.0" +edition = "2021" + +#[[bin]] +#name = "revme" +#path = "src/lib.rs" + +[dependencies] +zkm-runtime = { path = "../../../runtime/entrypoint" } +revm = { git = "https://github.com/powdr-labs/revm", branch = "serde-no-std", default-features = false, features = [ "serde" ] } + +#models = { path = "../models" } +models = { git = "https://github.com/eigmax/powdr-revme", branch = "continuations", package = "models" } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive", "rc"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +k256 = { version = "0.13.3", features = ["ecdsa"], default-features = false } + +# TODO can be removed once the powdr RISCV nightly is updated +ahash = { version = "=0.8.6", default-features = false } + +libc = { version = "0.2" , features = ["extra_traits"] } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/guest-program/revme/rust-toolchain.toml b/guest-program/revme/rust-toolchain.toml new file mode 100644 index 00000000..be71855e --- /dev/null +++ b/guest-program/revme/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2023-03-06" +targets = ["mips-unknown-linux-musl"] +profile = "minimal" diff --git a/guest-program/revme/src/main.rs b/guest-program/revme/src/main.rs new file mode 100644 index 00000000..412443b3 --- /dev/null +++ b/guest-program/revme/src/main.rs @@ -0,0 +1,190 @@ +#![no_std] +#![no_main] + +use revm::{ + db::CacheState, + interpreter::CreateScheme, + primitives::{calc_excess_blob_gas, keccak256, Bytecode, Env, SpecId, TransactTo, U256}, + Evm, +}; +extern crate libc; + +use models::*; + +mod utils; + +use utils::recover_address; + +extern crate alloc; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::collections::BTreeMap; + +zkm_runtime::entrypoint!(main); + +pub fn main() { + ethereum_test(); +} + +fn ethereum_test() { + let input: Vec = zkm_runtime::io::read(); + let suite = read_suite(&input); + + assert!(execute_test_suite(suite).is_ok()); +} + +fn read_suite(s: &Vec) -> TestSuite { + let suite: TestUnit = serde_json::from_slice(s).map_err(|e| e).unwrap(); + let mut btm = BTreeMap::new(); + btm.insert("test".to_string(), suite); + TestSuite(btm) +} + +fn execute_test_suite(suite: TestSuite) -> Result<(), String> { + for (_name, unit) in suite.0 { + // Create database and insert cache + let mut cache_state = CacheState::new(false); + for (address, info) in unit.pre { + let acc_info = revm::primitives::AccountInfo { + balance: info.balance, + code_hash: keccak256(&info.code), + code: Some(Bytecode::new_raw(info.code)), + nonce: info.nonce, + }; + cache_state.insert_account_with_storage(address, acc_info, info.storage); + } + + let mut env = Env::default(); + // for mainnet + env.cfg.chain_id = 1; + // env.cfg.spec_id is set down the road + + // block env + env.block.number = unit.env.current_number; + env.block.coinbase = unit.env.current_coinbase; + env.block.timestamp = unit.env.current_timestamp; + env.block.gas_limit = unit.env.current_gas_limit; + env.block.basefee = unit.env.current_base_fee.unwrap_or_default(); + env.block.difficulty = unit.env.current_difficulty; + // after the Merge prevrandao replaces mix_hash field in block and replaced difficulty opcode in EVM. + env.block.prevrandao = unit.env.current_random; + // EIP-4844 + if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = ( + unit.env.parent_blob_gas_used, + unit.env.parent_excess_blob_gas, + ) { + env.block + .set_blob_excess_gas_and_price(calc_excess_blob_gas( + parent_blob_gas_used.to(), + parent_excess_blob_gas.to(), + )); + } + + // tx env + env.tx.caller = match unit.transaction.sender { + Some(address) => address, + _ => recover_address(unit.transaction.secret_key.as_slice()) + .ok_or_else(|| String::new())?, + }; + env.tx.gas_price = unit + .transaction + .gas_price + .or(unit.transaction.max_fee_per_gas) + .unwrap_or_default(); + env.tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; + // EIP-4844 + env.tx.blob_hashes = unit.transaction.blob_versioned_hashes; + env.tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; + + // post and execution + for (spec_name, tests) in unit.post { + if matches!( + spec_name, + SpecName::ByzantiumToConstantinopleAt5 + | SpecName::Constantinople + | SpecName::Unknown + ) { + continue; + } + + let spec_id = spec_name.to_spec_id(); + + for (_index, test) in tests.into_iter().enumerate() { + env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); + + env.tx.data = unit + .transaction + .data + .get(test.indexes.data) + .unwrap() + .clone(); + env.tx.value = unit.transaction.value[test.indexes.value]; + + env.tx.access_list = unit + .transaction + .access_lists + .get(test.indexes.data) + .and_then(Option::as_deref) + .unwrap_or_default() + .iter() + .map(|item| { + ( + item.address, + item.storage_keys + .iter() + .map(|key| U256::from_be_bytes(key.0)) + .collect::>(), + ) + }) + .collect(); + + let to = match unit.transaction.to { + Some(add) => TransactTo::Call(add), + None => TransactTo::Create(CreateScheme::Create), + }; + env.tx.transact_to = to; + + let mut cache = cache_state.clone(); + cache.set_state_clear_flag(SpecId::enabled( + spec_id, + revm::primitives::SpecId::SPURIOUS_DRAGON, + )); + let mut state = revm::db::State::builder() + .with_cached_prestate(cache) + .with_bundle_update() + .build(); + let mut evm = Evm::builder() + .with_db(&mut state) + .modify_env(|e| *e = env.clone()) + .spec_id(spec_id) + .build(); + + // do the deed + //let timer = Instant::now(); + let mut check = || { + let exec_result = evm.transact_commit(); + + match (&test.expect_exception, &exec_result) { + // do nothing + (None, Ok(_)) => (), + // return okay, exception is expected. + (Some(_), Err(_e)) => { + return Ok(()); + } + _ => { + let s = exec_result.clone().err().map(|e| e.to_string()).unwrap(); + return Err(s); + } + } + Ok(()) + }; + + let Err(e) = check() else { continue }; + + return Err(e); + } + } + } + Ok(()) +} diff --git a/guest-program/revme/src/utils.rs b/guest-program/revme/src/utils.rs new file mode 100644 index 00000000..c06310aa --- /dev/null +++ b/guest-program/revme/src/utils.rs @@ -0,0 +1,25 @@ +use k256::ecdsa::SigningKey; +use revm::primitives::Address; + +/// Recover the address from a private key (SigningKey). +pub fn recover_address(private_key: &[u8]) -> Option
{ + let key = SigningKey::from_slice(private_key).ok()?; + let public_key = key.verifying_key().to_encoded_point(false); + Some(Address::from_raw_public_key(&public_key.as_bytes()[1..])) +} + +#[cfg(test)] +mod tests { + use super::*; + use revm::primitives::{address, hex}; + + #[test] + fn sanity_test() { + assert_eq!( + Some(address!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b")), + recover_address(&hex!( + "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + )) + ) + } +} diff --git a/host-program/Cargo.toml b/host-program/Cargo.toml new file mode 100644 index 00000000..75cfbc48 --- /dev/null +++ b/host-program/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "zkm-host-program" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "add-go-prove" +path = "src/bin/add-go-prove.rs" + +[[bin]] +name = "revme-prove" +path = "src/bin/revme-prove.rs" + +[[bin]] +name = "revme-network-prove" +path = "src/bin/revme-network-prove.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +##zkm-emulator = { path = "../emulator" } +#plonky2 = { path = "../plonky2/plonky2" } +##starky = { path = "../plonky2/starky" } +#plonky2_util = { path = "../plonky2/util" } +#plonky2_maybe_rayon = { path = "../plonky2/maybe_rayon" } + +bincode = "1.3.3" + +stage-service = {package = "service", git = "https://github.com/zkMIPS/zkm-prover", branch = "main", default-features = false } +zkm-prover = { git = "https://github.com/zkMIPS/zkm", branch = "main", default-features = false } +zkm-emulator = { git = "https://github.com/zkMIPS/zkm", branch = "main", default-features = false } +common = { git = "https://github.com/zkMIPS/zkm-prover", branch = "main", default-features = false } +plonky2 = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } +#starky = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } +plonky2_util = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } +plonky2_maybe_rayon = { git = "https://github.com/zkMIPS/plonky2.git", branch = "zkm_dev" } + +tonic = "0.8.1" +prost = "0.11.0" +tokio = { version = "1.21.0", features = ["macros", "rt-multi-thread", "signal"] } +ethers = "2.0.14" + +itertools = "0.11.0" +log = { version = "0.4.14", default-features = false } +anyhow = "1.0.75" +num = "0.4.0" +num-bigint = "0.4.3" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0" +tiny-keccak = "2.0.2" +rand = "0.8.5" +rand_chacha = "0.3.1" +once_cell = "1.13.0" +static_assertions = "1.1.0" +byteorder = "1.5.0" +hex = "0.4" +hashbrown = { version = "0.14.0", default-features = false, features = ["ahash", "serde"] } # NOTE: When upgrading, see `ahash` dependency. +lazy_static = "1.4.0" + +elf = { version = "0.7", default-features = false } +uuid = { version = "1.2", features = ["v4", "fast-rng", "macro-diagnostics"] } + +##[dev-dependencies] +env_logger = "0.10.0" +keccak-hash = "0.10.0" +plonky2x = { git = "https://github.com/zkMIPS/succinctx.git", package = "plonky2x", branch = "zkm" } +plonky2x-derive = { git = "https://github.com/zkMIPS/succinctx.git", package = "plonky2x-derive", branch = "zkm" } + +[build-dependencies] +tonic-build = "0.8.0" + +[features] +test = [] diff --git a/host-program/build.rs b/host-program/build.rs new file mode 100644 index 00000000..24afffaf --- /dev/null +++ b/host-program/build.rs @@ -0,0 +1,5 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("proto/src/proto/prover/v1/prover.proto")?; + tonic_build::compile_protos("proto/src/proto/stage/v1/stage.proto")?; + Ok(()) +} \ No newline at end of file diff --git a/host-program/proto/include/google/protobuf/empty.proto b/host-program/proto/include/google/protobuf/empty.proto new file mode 100644 index 00000000..0ada0f69 --- /dev/null +++ b/host-program/proto/include/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} \ No newline at end of file diff --git a/host-program/proto/src/proto/prover/v1/prover.proto b/host-program/proto/src/proto/prover/v1/prover.proto new file mode 100644 index 00000000..4c3a4a47 --- /dev/null +++ b/host-program/proto/src/proto/prover/v1/prover.proto @@ -0,0 +1,173 @@ +syntax = "proto3"; + +package prover.v1; + +message Version { + string v0_0_1 = 1; +} + +// timestamps are represented in unix time in seconds + +enum ResultCode { + OK = 0; + INVALID_PARAMETER = 1; + INTERNAL_ERROR = 2; + BUSY = 3; + UNSPECIFIED = 4; +} + +message Result { + ResultCode code = 1; + string message = 2; +} + +service ProverService { + rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {} + rpc GetTaskResult(GetTaskResultRequest) returns (GetTaskResultResponse) {} + rpc SplitElf(SplitElfRequest) returns (SplitElfResponse) {} + rpc Prove(ProveRequest) returns (ProveResponse) {} + rpc Aggregate(AggregateRequest) returns (AggregateResponse) {} + rpc AggregateAll(AggregateAllRequest) returns (AggregateAllResponse) {} + rpc FinalProof(FinalProofRequest) returns (FinalProofResponse) {} +} + +message GetStatusRequest {} + +message GetStatusResponse { + enum Status { + STATUS_UNSPECIFIED = 0; + STATUS_BOOTING = 1; + STATUS_COMPUTING = 2; + STATUS_IDLE = 3; + STATUS_HALT = 4; + } + Status status = 1; + string prover_name = 2; + string prover_id = 3; + uint64 number_of_cores = 4; + uint64 total_memory = 5; + uint64 free_memory = 6; +} + +message GetTaskResultRequest { + string proof_id = 1; + // uuid + string computed_request_id = 2; +} + +message GetTaskResultResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} + +message SplitElfRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + string computed_request_id = 4; + + string base_dir = 5; + string elf_path = 6; + string seg_path = 7; + uint64 block_no = 8; + uint32 seg_size = 9; + string args = 10; + string public_input_path = 11; + string private_input_path = 12; + string output_path = 13; +} + +message SplitElfResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} + +message ProveRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + string computed_request_id = 4; + + string base_dir = 5; + string seg_path = 6; + uint64 block_no = 7; + uint32 seg_size = 8; + string proof_path = 9; + string pub_value_path = 10; +} + +message ProveResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} + +message AggregateInput { + string proof_path = 1; + string pub_value_path = 2; + bool is_agg = 3; +}; + +message AggregateRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + string computed_request_id = 4; + + string base_dir = 5; + string seg_path = 6; + uint64 block_no = 7; + uint32 seg_size = 8; + AggregateInput input1 = 9; + AggregateInput input2 = 10; + string agg_proof_path = 11; + string agg_pub_value_path = 12; + bool is_final = 13; + string output_dir = 14; +} + +message AggregateResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} + +message AggregateAllRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + string computed_request_id = 4; + + string base_dir = 5; + string seg_path = 6; + uint64 block_no = 7; + uint32 seg_size = 8; + uint32 proof_num = 9; + string proof_dir = 10; + string pub_value_dir = 11; + string output_dir = 12; +} + +message AggregateAllResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} + +message FinalProofRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + string computed_request_id = 4; + bytes common_circuit_data = 5; + bytes verifier_only_circuit_data = 6; + bytes proof_with_public_inputs = 7; +} + +message FinalProofResponse { + string proof_id = 1; + string computed_request_id = 2; + Result result = 3; +} diff --git a/host-program/proto/src/proto/stage/v1/stage.proto b/host-program/proto/src/proto/stage/v1/stage.proto new file mode 100644 index 00000000..8003d106 --- /dev/null +++ b/host-program/proto/src/proto/stage/v1/stage.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; + +package stage.v1; + + +service StageService { + rpc GenerateProof(GenerateProofRequest) returns (GenerateProofResponse) {} + rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {} +} + +enum Status { + SUCCESS = 0; + UNSPECIFIED = 1; + COMPUTING = 2; + INVALID_PARAMETER = 3; + INTERNAL_ERROR = 4; + SPLIT_ERROR = 5; + PROVE_ERROR = 6; + AGG_ERROR = 7; + FINAL_ERROR = 8; +} + +message BlockFileItem { + string file_name = 1; + bytes file_content = 2; +} + +message GenerateProofRequest { + uint64 chain_id = 1; + uint64 timestamp = 2; + string proof_id = 3; + bytes elf_data = 4; + repeated BlockFileItem block_data = 5; + uint64 block_no = 6; + uint32 seg_size = 7; + string args = 8; + string signature = 9; + bytes public_input_stream = 10; + bytes private_input_stream = 11; + bool execute_only = 12; +} + +message GenerateProofResponse { + uint32 status = 1; + string error_message = 2; + string proof_id = 3; + string proof_url = 4; + string stark_proof_url = 5; + string solidity_verifier_url = 6; + bytes output_stream = 7; +} + +message GetStatusRequest { + string proof_id = 1; +} + +message GetStatusResponse { + string proof_id = 1; + uint32 status = 2; + bytes proof_with_public_inputs = 3; + string proof_url = 4; + string stark_proof_url = 5; + string solidity_verifier_url = 6; + bytes output_stream = 7; +} \ No newline at end of file diff --git a/host-program/src/bin/add-go-prove.rs b/host-program/src/bin/add-go-prove.rs new file mode 100644 index 00000000..b60afcc6 --- /dev/null +++ b/host-program/src/bin/add-go-prove.rs @@ -0,0 +1,322 @@ +use serde::{Deserialize, Serialize}; +use std::env; +use std::fs::File; + +use std::io::BufReader; +use std::ops::Range; +use std::time::Duration; + +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; +use plonky2::util::timing::TimingTree; +use plonky2x::backend::circuit::Groth16WrapperParameters; +use plonky2x::backend::wrapper::wrap::WrappedCircuit; +use plonky2x::frontend::builder::CircuitBuilder as WrapperBuilder; +use plonky2x::prelude::DefaultParameters; +use zkm_emulator::utils::{ + load_elf_with_patch, split_prog_into_segs, +}; +use zkm_prover::all_stark::AllStark; +use zkm_prover::config::StarkConfig; +use zkm_prover::cpu::kernel::assembler::segment_kernel; +use zkm_prover::fixed_recursive_verifier::AllRecursiveCircuits; +use zkm_prover::proof; +use zkm_prover::proof::PublicValues; +use zkm_prover::prover::prove; +use zkm_prover::verifier::verify_proof; + +const DEGREE_BITS_RANGE: [Range; 6] = [10..21, 12..22, 12..21, 8..21, 6..21, 13..23]; + + +fn prove_single_seg_common( + seg_file: &str, + basedir: &str, + block: &str, + file: &str, + seg_size: usize, +) { + let seg_reader = BufReader::new(File::open(seg_file).unwrap()); + let kernel = segment_kernel(basedir, block, file, seg_reader, seg_size); + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let allstark: AllStark = AllStark::default(); + let config = StarkConfig::standard_fast_config(); + let mut timing = TimingTree::new("prove", log::Level::Info); + let allproof: proof::AllProof = + prove(&allstark, &kernel, &config, &mut timing).unwrap(); + let mut count_bytes = 0; + for (row, proof) in allproof.stark_proofs.clone().iter().enumerate() { + let proof_str = serde_json::to_string(&proof.proof).unwrap(); + log::info!("row:{} proof bytes:{}", row, proof_str.len()); + count_bytes += proof_str.len(); + } + timing.filter(Duration::from_millis(100)).print(); + log::info!("total proof bytes:{}KB", count_bytes / 1024); + verify_proof(&allstark, allproof, &config).unwrap(); + log::info!("Prove done"); +} + +fn prove_multi_seg_common( + seg_dir: &str, + basedir: &str, + block: &str, + file: &str, + seg_size: usize, + seg_file_number: usize, + seg_start_id: usize, +) -> anyhow::Result<()> { + type InnerParameters = DefaultParameters; + type OuterParameters = Groth16WrapperParameters; + + type F = GoldilocksField; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + + if seg_file_number < 2 { + panic!("seg file number must >= 2\n"); + } + + let total_timing = TimingTree::new("prove total time", log::Level::Info); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + // Preprocess all circuits. + let all_circuits = + AllRecursiveCircuits::::new(&all_stark, &DEGREE_BITS_RANGE, &config); + + let seg_file = format!("{}/{}", seg_dir, seg_start_id); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file)?); + let input_first = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (mut agg_proof, mut updated_agg_public_values) = + all_circuits.prove_root(&all_stark, &input_first, &config, &mut timing)?; + + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(agg_proof.clone())?; + + let mut base_seg = seg_start_id + 1; + let mut seg_num = seg_file_number - 1; + let mut is_agg = false; + + if seg_file_number % 2 == 0 { + let seg_file = format!("{}/{}", seg_dir, seg_start_id + 1); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file)?); + let input = segment_kernel(basedir, block, file, seg_reader, seg_size); + timing = TimingTree::new("prove root second", log::Level::Info); + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &input, &config, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_root(root_proof.clone())?; + + // Update public values for the aggregation. + let agg_public_values = PublicValues { + roots_before: updated_agg_public_values.roots_before, + roots_after: public_values.roots_after, + userdata: public_values.userdata, + }; + timing = TimingTree::new("prove aggression", log::Level::Info); + // We can duplicate the proofs here because the state hasn't mutated. + (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( + false, + &agg_proof, + false, + &root_proof, + agg_public_values.clone(), + )?; + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_aggregation(&agg_proof)?; + + is_agg = true; + base_seg = seg_start_id + 2; + seg_num -= 1; + } + + for i in 0..seg_num / 2 { + let seg_file = format!("{}/{}", seg_dir, base_seg + (i << 1)); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(&seg_file)?); + let input_first = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (root_proof_first, first_public_values) = + all_circuits.prove_root(&all_stark, &input_first, &config, &mut timing)?; + + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(root_proof_first.clone())?; + + let seg_file = format!("{}/{}", seg_dir, base_seg + (i << 1) + 1); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(&seg_file)?); + let input = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root second", log::Level::Info); + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &input, &config, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_root(root_proof.clone())?; + + // Update public values for the aggregation. + let new_agg_public_values = PublicValues { + roots_before: first_public_values.roots_before, + roots_after: public_values.roots_after, + userdata: public_values.userdata, + }; + timing = TimingTree::new("prove aggression", log::Level::Info); + // We can duplicate the proofs here because the state hasn't mutated. + let (new_agg_proof, new_updated_agg_public_values) = all_circuits.prove_aggregation( + false, + &root_proof_first, + false, + &root_proof, + new_agg_public_values, + )?; + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_aggregation(&new_agg_proof)?; + + // Update public values for the nested aggregation. + let agg_public_values = PublicValues { + roots_before: updated_agg_public_values.roots_before, + roots_after: new_updated_agg_public_values.roots_after, + userdata: new_updated_agg_public_values.userdata, + }; + timing = TimingTree::new("prove nested aggression", log::Level::Info); + + // We can duplicate the proofs here because the state hasn't mutated. + (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( + is_agg, + &agg_proof, + true, + &new_agg_proof, + agg_public_values.clone(), + )?; + is_agg = true; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_aggregation(&agg_proof)?; + } + + let (block_proof, _block_public_values) = + all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?; + + log::info!( + "proof size: {:?}", + serde_json::to_string(&block_proof.proof).unwrap().len() + ); + let result = all_circuits.verify_block(&block_proof); + + let build_path = "verifier/data".to_string(); + let path = format!("{}/test_circuit/", build_path); + let builder = WrapperBuilder::::new(); + let mut circuit = builder.build(); + circuit.set_data(all_circuits.block.circuit); + let mut bit_size = vec![32usize; 16]; + bit_size.extend(vec![8; 32]); + bit_size.extend(vec![64; 68]); + let wrapped_circuit = WrappedCircuit::::build( + circuit, + Some((vec![], bit_size)), + ); + log::info!("build finish"); + + let wrapped_proof = wrapped_circuit.prove(&block_proof).unwrap(); + wrapped_proof.save(path).unwrap(); + + total_timing.filter(Duration::from_millis(100)).print(); + result +} + + + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum DataId { + TYPE1, + TYPE2, + TYPE3, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Data { + pub input1: [u8; 10], + pub input2: u8, + pub input3: i8, + pub input4: u16, + pub input5: i16, + pub input6: u32, + pub input7: i32, + pub input8: u64, + pub input9: i64, + pub input10: Vec, + pub input11: DataId, + pub input12: String, +} + +impl Default for Data { + fn default() -> Self { + Self::new() + } +} + +impl Data { + pub fn new() -> Self { + let array = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8]; + Self { + input1: array, + input2: 0x11u8, + input3: -1i8, + input4: 0x1122u16, + input5: -1i16, + input6: 0x112233u32, + input7: -1i32, + input8: 0x1122334455u64, + input9: -1i64, + input10: array[1..3].to_vec(), + input11: DataId::TYPE3, + input12: "hello".to_string(), + } + } +} + + + + +fn main() { + env_logger::try_init().unwrap_or_default(); + // 1. split ELF into segs + let elf_path = env::var("ELF_PATH").unwrap_or("guest-program/mips-elf/zkm-mips-elf-add-go".to_string()); + let seg_path = env::var("SEG_OUTPUT").expect("Segment output path is missing"); + let seg_size = env::var("SEG_SIZE").unwrap_or("131072".to_string()); + let seg_size = seg_size.parse::<_>().unwrap_or(0); + + let mut state = load_elf_with_patch(&elf_path, vec![]); + + let data = Data::new(); + state.add_input_stream(&data); + log::info!( + "enum {} {} {}", + DataId::TYPE1 as u8, + DataId::TYPE2 as u8, + DataId::TYPE3 as u8 + ); + log::info!("public input: {:X?}", data); + + let (total_steps, mut state) = split_prog_into_segs(state, &seg_path, "", seg_size); + + let value = state.read_public_values::(); + log::info!("public value: {:X?}", value); + + let mut seg_num = 1usize; + if seg_size != 0 { + seg_num = (total_steps + seg_size - 1) / seg_size; + } + + if seg_num == 1 { + let seg_file = format!("{seg_path}/{}", 0); + prove_single_seg_common(&seg_file, "", "", "", total_steps) + } else { + prove_multi_seg_common(&seg_path, "", "", "", seg_size, seg_num, 0).unwrap() + } +} diff --git a/host-program/src/bin/revme-network-prove.rs b/host-program/src/bin/revme-network-prove.rs new file mode 100644 index 00000000..d7fa2d33 --- /dev/null +++ b/host-program/src/bin/revme-network-prove.rs @@ -0,0 +1,159 @@ +use common::file; +use common::tls::Config; +use stage_service::stage_service_client::StageServiceClient; +use stage_service::{BlockFileItem, GenerateProofRequest, GetStatusRequest}; +use std::env; +use std::path::Path; +use std::time::Instant; +use tokio::time; +use tonic::transport::ClientTlsConfig; +use tonic::transport::Endpoint; + +use ethers::signers::{LocalWallet, Signer}; + +pub mod stage_service { + tonic::include_proto!("stage.v1"); +} + +async fn sign_ecdsa(request: &mut GenerateProofRequest, private_key: &str) { + if !private_key.is_empty() { + log::info!("signature, proof_id: {:?}", request.proof_id); + let wallet = private_key.parse::().unwrap(); + let sign_data = format!( + "{}&{}&{}&{}", + request.proof_id, request.block_no, request.seg_size, request.args + ); + let signature = wallet.sign_message(sign_data).await.unwrap(); + request.signature = signature.to_string(); + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::try_init().unwrap_or_default(); + let elf_path = env::var("ELF_PATH").unwrap_or("guest-program/mips-elf/zkm-mips-elf-revme-rust".to_string()); + let output_dir = env::var("OUTPUT_DIR").unwrap_or("/tmp/zkm".to_string()); + let block_path = env::var("BLOCK_PATH").unwrap_or("".to_string()); + let block_no = env::var("BLOCK_NO").unwrap_or("0".to_string()); + let block_no = block_no.parse::<_>().unwrap_or(0); + let seg_size = env::var("SEG_SIZE").unwrap_or("131072".to_string()); + let seg_size = seg_size.parse::<_>().unwrap_or(131072); + let args = env::var("ARGS").unwrap_or("".to_string()); + let public_input_path = env::var("PUBLIC_INPUT_PATH").unwrap_or("".to_string()); + let private_input_path = env::var("PRIVATE_INPUT_PATH").unwrap_or("".to_string()); + let endpoint = env::var("ENDPOINT").unwrap_or("http://127.0.0.1:50000".to_string()); + let ca_cert_path = env::var("CA_CERT_PATH").unwrap_or("".to_string()); + let cert_path = env::var("CERT_PATH").unwrap_or("".to_string()); + let key_path = env::var("KEY_PATH").unwrap_or("".to_string()); + let domain_name = env::var("DOMAIN_NAME").unwrap_or("stage".to_string()); + let private_key = env::var("PRIVATE_KEY").unwrap_or("".to_string()); + let execute_only = env::var("EXECUTE_ONLY").unwrap_or("false".to_string()); + let execute_only = execute_only.parse::().unwrap_or(false); + let ssl_config = if ca_cert_path.is_empty() { + None + } else { + Some(Config::new(ca_cert_path, cert_path, key_path).await?) + }; + + let elf_data = file::new(&elf_path).read().unwrap(); + let mut block_data = Vec::new(); + + if block_no > 0 { + let files = file::new(&block_path).read_dir().unwrap(); + for file_name in files { + let file_path = format!("{}/{}", block_path, file_name); + let block_file_item = BlockFileItem { + file_name: file_name.to_string(), + file_content: file::new(&file_path).read().unwrap(), + }; + block_data.push(block_file_item); + } + } + + let public_input_stream = if public_input_path.is_empty() { + vec![] + } else { + file::new(&public_input_path).read().unwrap() + }; + + let private_input_stream = if private_input_path.is_empty() { + vec![] + } else { + file::new(&private_input_path).read().unwrap() + }; + + let proof_id = uuid::Uuid::new_v4().to_string(); + let mut request = GenerateProofRequest { + proof_id: proof_id.clone(), + elf_data, + block_data, + block_no, + seg_size, + args, + public_input_stream, + private_input_stream, + execute_only, + ..Default::default() + }; + sign_ecdsa(&mut request, &private_key).await; + log::info!("request: {:?}", proof_id); + let start = Instant::now(); + let endpoint = match ssl_config { + Some(config) => { + let mut tls_config = ClientTlsConfig::new().domain_name(domain_name); + if let Some(ca_cert) = config.ca_cert { + tls_config = tls_config.ca_certificate(ca_cert); + } + if let Some(identity) = config.identity { + tls_config = tls_config.identity(identity); + } + Endpoint::new(endpoint)?.tls_config(tls_config)? + } + None => Endpoint::new(endpoint)?, + }; + let mut stage_client = StageServiceClient::connect(endpoint).await?; + let response = stage_client.generate_proof(request).await?.into_inner(); + log::info!("generate_proof response: {:?}", response); + if response.status == crate::stage_service::Status::Computing as u32 { + loop { + let get_status_request = GetStatusRequest { + proof_id: proof_id.clone(), + }; + let get_status_response = stage_client + .get_status(get_status_request) + .await? + .into_inner(); + if get_status_response.status != crate::stage_service::Status::Computing as u32 { + if let Some(status) = + crate::stage_service::Status::from_i32(get_status_response.status as i32) + { + match status { + crate::stage_service::Status::Success => { + log::info!( + "generate_proof success public_inputs_size: {}, output_size: {}", + get_status_response.proof_with_public_inputs.len(), + get_status_response.output_stream.len(), + ); + let output_dir = Path::new(&output_dir); + let public_inputs_path = output_dir.join("proof_with_public_inputs"); + let _ = file::new(&public_inputs_path.to_string_lossy()) + .write(get_status_response.proof_with_public_inputs.as_slice()); + } + _ => { + log::info!( + "generate_proof failed status: {}", + get_status_response.status + ); + } + } + } + break; + } + time::sleep(time::Duration::from_secs(1)).await; + } + } + let end = Instant::now(); + let elapsed = end.duration_since(start); + log::info!("Elapsed time: {:?} secs", elapsed.as_secs()); + Ok(()) +} diff --git a/host-program/src/bin/revme-prove.rs b/host-program/src/bin/revme-prove.rs new file mode 100644 index 00000000..3d7d72a4 --- /dev/null +++ b/host-program/src/bin/revme-prove.rs @@ -0,0 +1,313 @@ +use serde::{Deserialize, Serialize}; +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::io::BufReader; +use std::ops::Range; +use std::time::Duration; + +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; +use plonky2::util::timing::TimingTree; +use plonky2x::backend::circuit::Groth16WrapperParameters; +use plonky2x::backend::wrapper::wrap::WrappedCircuit; +use plonky2x::frontend::builder::CircuitBuilder as WrapperBuilder; +use plonky2x::prelude::DefaultParameters; +use zkm_emulator::utils::{ + load_elf_with_patch, split_prog_into_segs, +}; +use zkm_prover::all_stark::AllStark; +use zkm_prover::config::StarkConfig; +use zkm_prover::cpu::kernel::assembler::segment_kernel; +use zkm_prover::fixed_recursive_verifier::AllRecursiveCircuits; +use zkm_prover::proof; +use zkm_prover::proof::PublicValues; +use zkm_prover::prover::prove; +use zkm_prover::verifier::verify_proof; + +const DEGREE_BITS_RANGE: [Range; 6] = [10..21, 12..22, 12..21, 8..21, 6..21, 13..23]; + +fn prove_single_seg_common( + seg_file: &str, + basedir: &str, + block: &str, + file: &str, + seg_size: usize, +) { + let seg_reader = BufReader::new(File::open(seg_file).unwrap()); + let kernel = segment_kernel(basedir, block, file, seg_reader, seg_size); + + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let allstark: AllStark = AllStark::default(); + let config = StarkConfig::standard_fast_config(); + let mut timing = TimingTree::new("prove", log::Level::Info); + let allproof: proof::AllProof = + prove(&allstark, &kernel, &config, &mut timing).unwrap(); + let mut count_bytes = 0; + for (row, proof) in allproof.stark_proofs.clone().iter().enumerate() { + let proof_str = serde_json::to_string(&proof.proof).unwrap(); + log::info!("row:{} proof bytes:{}", row, proof_str.len()); + count_bytes += proof_str.len(); + } + timing.filter(Duration::from_millis(100)).print(); + log::info!("total proof bytes:{}KB", count_bytes / 1024); + verify_proof(&allstark, allproof, &config).unwrap(); + log::info!("Prove done"); +} + +fn prove_multi_seg_common( + seg_dir: &str, + basedir: &str, + block: &str, + file: &str, + seg_size: usize, + seg_file_number: usize, + seg_start_id: usize, +) -> anyhow::Result<()> { + type InnerParameters = DefaultParameters; + type OuterParameters = Groth16WrapperParameters; + + type F = GoldilocksField; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + + if seg_file_number < 2 { + panic!("seg file number must >= 2\n"); + } + + let total_timing = TimingTree::new("prove total time", log::Level::Info); + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + // Preprocess all circuits. + let all_circuits = + AllRecursiveCircuits::::new(&all_stark, &DEGREE_BITS_RANGE, &config); + + let seg_file = format!("{}/{}", seg_dir, seg_start_id); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file)?); + let input_first = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (mut agg_proof, mut updated_agg_public_values) = + all_circuits.prove_root(&all_stark, &input_first, &config, &mut timing)?; + + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(agg_proof.clone())?; + + let mut base_seg = seg_start_id + 1; + let mut seg_num = seg_file_number - 1; + let mut is_agg = false; + + if seg_file_number % 2 == 0 { + let seg_file = format!("{}/{}", seg_dir, seg_start_id + 1); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file)?); + let input = segment_kernel(basedir, block, file, seg_reader, seg_size); + timing = TimingTree::new("prove root second", log::Level::Info); + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &input, &config, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_root(root_proof.clone())?; + + // Update public values for the aggregation. + let agg_public_values = PublicValues { + roots_before: updated_agg_public_values.roots_before, + roots_after: public_values.roots_after, + userdata: public_values.userdata, + }; + timing = TimingTree::new("prove aggression", log::Level::Info); + // We can duplicate the proofs here because the state hasn't mutated. + (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( + false, + &agg_proof, + false, + &root_proof, + agg_public_values.clone(), + )?; + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_aggregation(&agg_proof)?; + + is_agg = true; + base_seg = seg_start_id + 2; + seg_num -= 1; + } + + for i in 0..seg_num / 2 { + let seg_file = format!("{}/{}", seg_dir, base_seg + (i << 1)); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(&seg_file)?); + let input_first = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (root_proof_first, first_public_values) = + all_circuits.prove_root(&all_stark, &input_first, &config, &mut timing)?; + + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(root_proof_first.clone())?; + + let seg_file = format!("{}/{}", seg_dir, base_seg + (i << 1) + 1); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(&seg_file)?); + let input = segment_kernel(basedir, block, file, seg_reader, seg_size); + let mut timing = TimingTree::new("prove root second", log::Level::Info); + let (root_proof, public_values) = + all_circuits.prove_root(&all_stark, &input, &config, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_root(root_proof.clone())?; + + // Update public values for the aggregation. + let new_agg_public_values = PublicValues { + roots_before: first_public_values.roots_before, + roots_after: public_values.roots_after, + userdata: public_values.userdata, + }; + timing = TimingTree::new("prove aggression", log::Level::Info); + // We can duplicate the proofs here because the state hasn't mutated. + let (new_agg_proof, new_updated_agg_public_values) = all_circuits.prove_aggregation( + false, + &root_proof_first, + false, + &root_proof, + new_agg_public_values, + )?; + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_aggregation(&new_agg_proof)?; + + // Update public values for the nested aggregation. + let agg_public_values = PublicValues { + roots_before: updated_agg_public_values.roots_before, + roots_after: new_updated_agg_public_values.roots_after, + userdata: new_updated_agg_public_values.userdata, + }; + timing = TimingTree::new("prove nested aggression", log::Level::Info); + + // We can duplicate the proofs here because the state hasn't mutated. + (agg_proof, updated_agg_public_values) = all_circuits.prove_aggregation( + is_agg, + &agg_proof, + true, + &new_agg_proof, + agg_public_values.clone(), + )?; + is_agg = true; + timing.filter(Duration::from_millis(100)).print(); + + all_circuits.verify_aggregation(&agg_proof)?; + } + + let (block_proof, _block_public_values) = + all_circuits.prove_block(None, &agg_proof, updated_agg_public_values)?; + + log::info!( + "proof size: {:?}", + serde_json::to_string(&block_proof.proof).unwrap().len() + ); + let result = all_circuits.verify_block(&block_proof); + + let build_path = "verifier/data".to_string(); + let path = format!("{}/test_circuit/", build_path); + let builder = WrapperBuilder::::new(); + let mut circuit = builder.build(); + circuit.set_data(all_circuits.block.circuit); + let mut bit_size = vec![32usize; 16]; + bit_size.extend(vec![8; 32]); + bit_size.extend(vec![64; 68]); + let wrapped_circuit = WrappedCircuit::::build( + circuit, + Some((vec![], bit_size)), + ); + log::info!("build finish"); + + let wrapped_proof = wrapped_circuit.prove(&block_proof).unwrap(); + wrapped_proof.save(path).unwrap(); + + total_timing.filter(Duration::from_millis(100)).print(); + result +} + + + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub enum DataId { + TYPE1, + TYPE2, + TYPE3, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Data { + pub input1: [u8; 10], + pub input2: u8, + pub input3: i8, + pub input4: u16, + pub input5: i16, + pub input6: u32, + pub input7: i32, + pub input8: u64, + pub input9: i64, + pub input10: Vec, + pub input11: DataId, + pub input12: String, +} + +impl Default for Data { + fn default() -> Self { + Self::new() + } +} + +impl Data { + pub fn new() -> Self { + let array = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8]; + Self { + input1: array, + input2: 0x11u8, + input3: -1i8, + input4: 0x1122u16, + input5: -1i16, + input6: 0x112233u32, + input7: -1i32, + input8: 0x1122334455u64, + input9: -1i64, + input10: array[1..3].to_vec(), + input11: DataId::TYPE3, + input12: "hello".to_string(), + } + } +} + + + +fn main() { + env_logger::try_init().unwrap_or_default(); + // 1. split ELF into segs + let elf_path = env::var("ELF_PATH").unwrap_or("guest-program/mips-elf/zkm-mips-elf-revme-rust".to_string()); + let seg_path = env::var("SEG_OUTPUT").expect("Segment output path is missing"); + let json_path = env::var("JSON_PATH").unwrap_or("host-program/test-vectors/test.json".to_string()); + let seg_size = env::var("SEG_SIZE").unwrap_or("131072".to_string()); + let seg_size = seg_size.parse::<_>().unwrap_or(0); + let mut f = File::open(json_path).unwrap(); + let mut data = vec![]; + f.read_to_end(&mut data).unwrap(); + + let mut state = load_elf_with_patch(&elf_path, vec![]); + // load input + state.add_input_stream(&data); + + let (total_steps, mut _state) = split_prog_into_segs(state, &seg_path, "", seg_size); + + let mut seg_num = 1usize; + if seg_size != 0 { + seg_num = (total_steps + seg_size - 1) / seg_size; + } + + if seg_num == 1 { + let seg_file = format!("{seg_path}/{}", 0); + prove_single_seg_common(&seg_file, "", "", "", total_steps) + } else { + prove_multi_seg_common(&seg_path, "", "", "", seg_size, seg_num, 0).unwrap() + } +} diff --git a/host-program/test-vectors/test.json b/host-program/test-vectors/test.json new file mode 100644 index 00000000..dccd9212 --- /dev/null +++ b/host-program/test-vectors/test.json @@ -0,0 +1,36 @@ +{ + "_info": null, + "env": { + "currentCoinbase": "0x0000000000000000000000000000000000000000", + "currentDifficulty": "0x400000000", + "currentGasLimit": "0x1388", + "currentNumber": "0x0", + "currentTimestamp": "0x0", + "currentBaseFee": "0x3b9aca00", + "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentBeaconRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentWithdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "parentBlobGasUsed": "0x0", + "parentExcessBlobGas": "0x0" + }, + "pre": {}, + "post": {}, + "transaction": { + "data": [], + "gasLimit": [], + "gasPrice": null, + "nonce": "0x0", + "secretKey": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sender": "0x0000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000", + "value": [], + "maxFeePerGas": null, + "maxPriorityFeePerGas": null, + "accessLists": [], + "blobVersionedHashes": [], + "maxFeePerBlobGas": null + }, + "out": null + } + \ No newline at end of file diff --git a/host-program/tool/.csr b/host-program/tool/.csr new file mode 100644 index 00000000..cead7232 --- /dev/null +++ b/host-program/tool/.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICjTCCAXUCAQAwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWb +wTpNHpDBeJOxDWkEq0rr9ljdOXKfQJY2knTwcqBv1Xer7zPGVlxgHHRiORtwZZx6 +v1cek241Tb+loJXrG7MBXZ0zcOY6XxwChpXrwZKH5EErFyJGU1i/1AG1M/z4z3vX +sfqYTtMFp/p9AfQhkteoITaSxUyPnl3UuEuOW+VtvOc8f0L23UUT7BbiW81JxnFz +6iaZi+QQEQ0QZftxqi/ivYv8D8Jw3muU9hRchVcIxvp4ewZdVOYMFvyrh4kBUGHS +X9m4bKwLPQqTs8xnVGpRfrKWJz3wPeVqKcGDbylvjgWMDp+oUW+4W6xDoNXqWKg9 +ecyJgZKfpLwZFA7RyLsCAwEAAaBIMEYGCSqGSIb3DQEJDjE5MDcwCQYDVR0TBAIw +ADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0G +CSqGSIb3DQEBCwUAA4IBAQAwiYnF3bx/qWWv55myToWVkG8nCCIV/yAF1PlPnZ2z +1JW4n3qIGJ3JoIZNjPiU08biST+CsLLhupp9XeD49jTkBgtNZCJ0vViW88OeYaMe +HHbgc2A020is4rEhSJy3hhvR7EYYpaYgdEqw37YY6WiWy9LE7s1x0Q3HQdxnpkgU +bHpTLMcpLyAIwvDkRKB0MvMdeEzR/fTcSZZvC4wS/CWSaQcwN816RCZPZJJVms+q +B/OcuFIG268ylbNr59itUwTOTtm11llyDzkzlwSUWAB5u+nDpJoFwh4fNR3iN9sQ +ciHwdDlNv1ii/JcglUmDmwT8TpTjXcWPmao1ONW/+UrM +-----END CERTIFICATE REQUEST----- diff --git a/host-program/tool/.key b/host-program/tool/.key new file mode 100644 index 00000000..2a238627 --- /dev/null +++ b/host-program/tool/.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtZvBOk0ekMF4k7ENaQSrSuv2WN05cp9AljaSdPByoG/Vd6vv +M8ZWXGAcdGI5G3BlnHq/Vx6TbjVNv6WglesbswFdnTNw5jpfHAKGlevBkofkQSsX +IkZTWL/UAbUz/PjPe9ex+phO0wWn+n0B9CGS16ghNpLFTI+eXdS4S45b5W285zx/ +QvbdRRPsFuJbzUnGcXPqJpmL5BARDRBl+3GqL+K9i/wPwnDea5T2FFyFVwjG+nh7 +Bl1U5gwW/KuHiQFQYdJf2bhsrAs9CpOzzGdUalF+spYnPfA95WopwYNvKW+OBYwO +n6hRb7hbrEOg1epYqD15zImBkp+kvBkUDtHIuwIDAQABAoIBABjsUS5RGxq+km+i +YrbA30ke7OXxJksms9RplNvbi7+c2Wzc+azfUUbIId+PA0tSt38sEUpBdfYv9oWQ +k2M/qsN8Mn66aooOBLNxXUsB2cTkRVz0Lu6TPCj32n6JgaL7+m2Cgnhjop2vLwhf +U39PhDl4Z2Z36f/BzfgOk2Q7Gz13AHDzpyFT8wJh/D3shGrqp9E7LUVWsFOkWQ+Y +qTzab4ASoRBuDLQGLWeq5Ksit/kBB2bw4W99eAZDjb+pOftj0ZReigGaUnljymNA +wBL1FgY6FpYNfO7foE5x00S5SthkuCQtk1Vk23hSeiua0tNwl/1rvdL6JnaBdwyi +X3JuUiECgYEA7vZvTFNyg8sajCnawWMoy9c9ftzHWZLe6aA0eOEisBuGWDOzYpyD +0WdcL6SYH4rV1+y7hMhg7xuu1pGdUbiy7EE9B384usyq2xAVbYNKJ/gnYCPACzM+ +if4IOWrv+/Cvf9a6AUWE91rWkFwnhjDhceoW7u5dWfcbkUnwKpjLXSMCgYEAwo58 +eXCnPG8vx2vl5IynInRT8yh2jcoHSvL8lBVAViw1pzxqOx8gakRerclUnmQNH3E8 ++skldKfvfw9gI1oTKNxwRmriZ10QPG8anWRZUT2BBgS60EMJKmxqtrZswImKsyGf +HdVShQcbcnKi1nBWR/kw+0cldAqjoEe1dkZw24kCgYEAkTFS9sFNNx7VBoz3ycEC +LLifONHUnmxK3RDmUPwaI37V5gmj2SrYPQfcT2Zmt+OXgxbn6QoHO8FKPj1DpT/g +FwScnE2Enpt0xJ3FJ0E6cYaqBhQyv5tnSFj9jkWXgzD+sWI6qkMfQ1wCOryDl1lj +4d8mCA700ZD/Qwv1zSwBhJkCgYEAmZ8woiNVqd6Rd4G3EPQOtKHt974nfmXbxE6B +Kdxt5J0b8GU/7FbznXVG3Spv6nFN+1nKsKtLLAW+kjFt0/w3zLfi33deNgfB3jZM +aaLm88MAsePiLrf/8MOSr5rz3EO+hRszLLdYZTnrbj8eXPxu5H3bM05GQI52DQUS +P8X3LyECgYEAgBiDyI4K+XADjMMGRmIJ44YF5VDtGELCUg6CT+0DPsf+p2tWlEC+ +eX5KL8ne7JlMrZDebSutBshwhRE8PsHQX9bnahXdR8+0uYgXTiZgbRUohuhWTzd6 +FUCmi7L3eGtmWoN33wKHmBymvWcz+AU7Ug28+cDPaqW0b8dag6OIZi8= +-----END RSA PRIVATE KEY----- diff --git a/host-program/tool/.pem b/host-program/tool/.pem new file mode 100644 index 00000000..9156a70e --- /dev/null +++ b/host-program/tool/.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC1TCCAb2gAwIBAgIULe/RnqpIa8ynqDoqT5j4hEQq5AwwDQYJKoZIhvcNAQEL +BQAwDjEMMAoGA1UEAwwDY2EtMB4XDTI0MDgyODAyNTI0NloXDTM0MDgyNjAyNTI0 +NlowADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWbwTpNHpDBeJOx +DWkEq0rr9ljdOXKfQJY2knTwcqBv1Xer7zPGVlxgHHRiORtwZZx6v1cek241Tb+l +oJXrG7MBXZ0zcOY6XxwChpXrwZKH5EErFyJGU1i/1AG1M/z4z3vXsfqYTtMFp/p9 +AfQhkteoITaSxUyPnl3UuEuOW+VtvOc8f0L23UUT7BbiW81JxnFz6iaZi+QQEQ0Q +Zftxqi/ivYv8D8Jw3muU9hRchVcIxvp4ewZdVOYMFvyrh4kBUGHSX9m4bKwLPQqT +s8xnVGpRfrKWJz3wPeVqKcGDbylvjgWMDp+oUW+4W6xDoNXqWKg9ecyJgZKfpLwZ +FA7RyLsCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw +FAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQBOrMTfqByW +jguNej0bAvKHy6SndLZ0gg5SqRtmxlF+WG4bTm8ANvSJKZzLeEomUPpJIsdavvef +r3/bVTxfns8MMKM+qF/tyAmDRDkm6M9fWI7aTfYWH6bBnr67A2rBWbCozi2nDvw3 +v3YUpVj755darbofMP+xDy+qE/oAQJQshA57Lb5YtdjbYBGZTXIiHEBqddjKMhDJ +3okoEysl3UI2rHaRijkkEU+RtMmF4e8tgHQR0QjY0VJlzGMx6i5mQltPjzOmEb+y +L/+RRUCGbRvHI3ZSyu74HNdYvoqBoWkTn2yQcJO5LdtmVGCizyHYeV0GeE6iwHyk +UqP+Y6ePYRHO +-----END CERTIFICATE----- diff --git a/host-program/tool/certgen.sh b/host-program/tool/certgen.sh new file mode 100755 index 00000000..599c58f3 --- /dev/null +++ b/host-program/tool/certgen.sh @@ -0,0 +1,161 @@ +#!/bin/bash -e + +CN='' +SSL_IP='' +SSL_DNS='' + +C=CN + +SSL_SIZE=2048 + +DATE=${DATE:-3650} + +SSL_CONFIG='openssl.cnf' + +help() { + cat <<-EOF + +Usage: ./certgen.sh [OPTIONS] COMMAND + +A script for zkm cert generation. + +Options: +--help Get the help info and exit +--cn Common name of the server +--ssl-ip Extended trust ips, such as 127.0.0.1, 0.0.0.0 +--ssl-dns Extended trust dns, such as demo.zkm.com +--ssl-size The key size +--date Validity of the certificate +--ssl-config Address of config file +EOF + exit 0 +} + +echo 'cn', $2 + +while [ -n "$1" ]; do + case "$1" in + --cn) + CN="$2" + shift + ;; + --ssl-ip) + SSL_IP="$2" + shift + ;; + --ssl-dns) + SSL_DNS="$2" + shift + ;; + --ssl-size) + SSL_SIZE=$2 + shift + ;; + --date) + DATE=$2 + shift + ;; + --ssl-config) + SSL_CONFIG="$2" + shift + ;; + -h | --help) + help + ;; + --) + shift + break + ;; + *) + echo "Error: not defined option." + exit 1 + ;; + esac + shift +done + +echo "----------------------------" +echo "| SSL Cert Generator |" +echo "----------------------------" +echo + +export CA_KEY=${CA_KEY-"ca.key"} +export CA_CERT=${CA_CERT-"ca.pem"} +export CA_SUBJECT=ca-$CN +export CA_EXPIRE=${DATE} + +export SSL_CONFIG=${SSL_CONFIG} +export SSL_KEY=$CN.key +export SSL_CSR=$CN.csr +export SSL_CERT=$CN.pem +export SSL_EXPIRE=${DATE} + +export SSL_SUBJECT=${CN} +export SSL_DNS=${SSL_DNS} +export SSL_IP=${SSL_IP} + +echo ${CA_SUBJECT} +echo ${CN} +echo "--> Certificate Authority" + +if [[ -e ./${CA_KEY} ]]; then + echo "====> Using existing CA Key ${CA_KEY}" +else + echo "====> Generating new CA key ${CA_KEY}" + openssl genrsa -out ${CA_KEY} ${SSL_SIZE} >/dev/null +fi + +if [[ -e ./${CA_CERT} ]]; then + echo "====> Using existing CA Certificate ${CA_CERT}" +else + echo "====> Generating new CA Certificate ${CA_CERT}" + openssl req -x509 -sha256 -new -nodes -key ${CA_KEY} \ + -days ${CA_EXPIRE} -out ${CA_CERT} -subj "/CN=${CA_SUBJECT}" >/dev/null || exit 1 +fi + +echo "====> Generating new config file ${SSL_CONFIG}" +cat >${SSL_CONFIG} <>${SSL_CONFIG} <>${SSL_CONFIG} + done + + if [[ -n ${SSL_IP} ]]; then + ip=(${SSL_IP}) + for i in "${!ip[@]}"; do + echo IP.$((i + 1)) = ${ip[$i]} >>${SSL_CONFIG} + done + fi +fi + +echo "====> Generating new SSL KEY ${SSL_KEY}" +openssl genrsa -out ${SSL_KEY} ${SSL_SIZE} >/dev/null || exit 1 + +echo "====> Generating new SSL CSR ${SSL_CSR}" +openssl req -sha256 -new -key ${SSL_KEY} -out ${SSL_CSR} \ + -subj "/CN=${SSL_SUBJECT}" -config ${SSL_CONFIG} >/dev/null || exit 1 + +echo "====> Generating new SSL CERT ${SSL_CERT}" +openssl x509 -sha256 -req -in ${SSL_CSR} -CA ${CA_CERT} \ + -CAkey ${CA_KEY} -CAcreateserial -out ${SSL_CERT} \ + -days ${SSL_EXPIRE} -extensions v3_req \ + -extfile ${SSL_CONFIG} >/dev/null || exit 1 + +echo "====> Complete" \ No newline at end of file diff --git a/testdata/mips/common_circuit_data.json b/testdata/mips/common_circuit_data.json new file mode 100644 index 00000000..e69de29b diff --git a/verifier/data/common_circuit_data.json b/verifier/data/common_circuit_data.json new file mode 100644 index 00000000..e69de29b diff --git a/verifier/verifier.sol.tmpl b/verifier/verifier.sol.tmpl new file mode 100644 index 00000000..9352be37 --- /dev/null +++ b/verifier/verifier.sol.tmpl @@ -0,0 +1,212 @@ +// This file is MIT Licensed. +// +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +pragma solidity ^0.8.0; +library Pairing { + struct G1Point { + uint X; + uint Y; + } + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint[2] X; + uint[2] Y; + } + /// @return the generator of G1 + function P1() pure internal returns (G1Point memory) { + return G1Point(1, 2); + } + /// @return the generator of G2 + function P2() pure internal returns (G2Point memory) { + return G2Point( + [10857046999023057135944570762232829481370756359578518086990519993285655852781, + 11559732032986387107991004021392285783925812861821192530917403151452391805634], + [8495653923123431417604973247489272438418190587263600148770280649306958101930, + 4082367875863433681332203403145435568316851327593401208105741076214120093531] + ); + } + /// @return the negation of p, i.e. p.addition(p.negate()) should be zero. + function negate(G1Point memory p) pure internal returns (G1Point memory) { + // The prime q in the base field F_q for G1 + uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + if (p.X == 0 && p.Y == 0) + return G1Point(0, 0); + return G1Point(p.X, q - (p.Y % q)); + } + /// @return r the sum of two points of G1 + function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { + uint[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require(success); + } + + + /// @return r the product of a point on G1 and a scalar, i.e. + /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p. + function scalar_mul(G1Point memory p, uint s) internal view returns (G1Point memory r) { + uint[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require (success); + } + /// @return the result of computing the pairing check + /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should + /// return true. + function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { + require(p1.length == p2.length); + uint elements = p1.length; + uint inputSize = elements * 6; + uint[] memory input = new uint[](inputSize); + for (uint i = 0; i < elements; i++) + { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[1]; + input[i * 6 + 3] = p2[i].X[0]; + input[i * 6 + 4] = p2[i].Y[1]; + input[i * 6 + 5] = p2[i].Y[0]; + } + uint[1] memory out; + bool success; + + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + // switch success case 0 { invalid() } + } + + require(success,"no"); + return out[0] != 0; + } + /// Convenience method for a pairing check for two pairs. + function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for three pairs. + function pairingProd3( + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](3); + G2Point[] memory p2 = new G2Point[](3); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + return pairing(p1, p2); + } + /// Convenience method for a pairing check for four pairs. + function pairingProd4( + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2, + G1Point memory d1, G2Point memory d2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](4); + G2Point[] memory p2 = new G2Point[](4); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p1[3] = d1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + p2[3] = d2; + return pairing(p1, p2); + } +} + +contract Verifier { + event VerifyEvent(address user); + event Value(uint x, uint y); + + using Pairing for *; + struct VerifyingKey { + Pairing.G1Point alpha; + Pairing.G2Point beta; + Pairing.G2Point gamma; + Pairing.G2Point delta; + Pairing.G1Point[] gamma_abc; + } + struct Proof { + Pairing.G1Point a; + Pairing.G2Point b; + Pairing.G1Point c; + } + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = {{.Alpha}}; + vk.beta = {{.Beta}}; + vk.gamma = {{.Gamma}}; + vk.delta = {{.Delta}}; + {{.Gamma_abc}} + } + function verify(uint[65] memory input, Proof memory proof, uint[2] memory proof_commitment) public view returns (uint) { + uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + VerifyingKey memory vk = verifyingKey(); + require(input.length + 1 == vk.gamma_abc.length); + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint i = 0; i < input.length; i++) { + require(input[i] < snark_scalar_field); + vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i])); + } + Pairing.G1Point memory p_c = Pairing.G1Point(proof_commitment[0], proof_commitment[1]); + + vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]); + vk_x = Pairing.addition(vk_x, p_c); + + if(!Pairing.pairingProd4( + proof.a, proof.b, + Pairing.negate(vk_x), vk.gamma, + Pairing.negate(proof.c), vk.delta, + Pairing.negate(vk.alpha), vk.beta)) { + return 1; + } + + return 0; + } + function verifyTx( + Proof memory proof, uint[65] memory input + ,uint[2] memory proof_commitment) public returns (bool r) { + + if (verify(input, proof , proof_commitment) == 0) { + emit VerifyEvent(msg.sender); + return true; + } else { + return false; + } + + } +}