From d42f71e4f7088686c09d3a5b8f091d6339838852 Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 19 Jun 2024 19:50:59 -0700 Subject: [PATCH 01/13] feat: deployment scripts --- Cargo.lock | 62 +++++++------- Cargo.toml | 2 +- contracts/.env.example | 28 +++++++ contracts/.gitignore | 9 +- contracts/deployments/11155111.json | 4 + contracts/deployments/421614.json | 4 + contracts/foundry.toml | 18 +++- contracts/script/SP1Verifier.s.sol | 12 --- contracts/script/deploy/SP1Verifier.s.sol | 27 ++++++ .../script/deploy/SP1VerifierGateway.sol | 22 +++++ contracts/script/utils/Base.s.sol | 82 +++++++++++++++++++ contracts/script/verify.sh | 71 ++++++++++++++++ 12 files changed, 291 insertions(+), 50 deletions(-) create mode 100644 contracts/.env.example create mode 100644 contracts/deployments/11155111.json create mode 100644 contracts/deployments/421614.json delete mode 100644 contracts/script/SP1Verifier.s.sol create mode 100644 contracts/script/deploy/SP1Verifier.s.sol create mode 100644 contracts/script/deploy/SP1VerifierGateway.sol create mode 100644 contracts/script/utils/Base.s.sol create mode 100755 contracts/script/verify.sh diff --git a/Cargo.lock b/Cargo.lock index db21c0e..903322c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p3-air" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3038,7 +3038,7 @@ dependencies = [ [[package]] name = "p3-baby-bear" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "num-bigint 0.4.5", "p3-field", @@ -3052,7 +3052,7 @@ dependencies = [ [[package]] name = "p3-blake3" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "blake3", "p3-symmetric", @@ -3061,7 +3061,7 @@ dependencies = [ [[package]] name = "p3-bn254-fr" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "ff 0.13.0", "num-bigint 0.4.5", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "p3-challenger" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -3087,7 +3087,7 @@ dependencies = [ [[package]] name = "p3-commit" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -3100,7 +3100,7 @@ dependencies = [ [[package]] name = "p3-dft" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "p3-field" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "num-bigint 0.4.5", @@ -3125,7 +3125,7 @@ dependencies = [ [[package]] name = "p3-fri" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -3143,7 +3143,7 @@ dependencies = [ [[package]] name = "p3-interpolation" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3153,7 +3153,7 @@ dependencies = [ [[package]] name = "p3-keccak" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-symmetric", "tiny-keccak", @@ -3162,7 +3162,7 @@ dependencies = [ [[package]] name = "p3-keccak-air" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-air", "p3-field", @@ -3175,7 +3175,7 @@ dependencies = [ [[package]] name = "p3-matrix" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-field", @@ -3189,7 +3189,7 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "rayon", ] @@ -3197,7 +3197,7 @@ dependencies = [ [[package]] name = "p3-mds" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-dft", @@ -3211,7 +3211,7 @@ dependencies = [ [[package]] name = "p3-merkle-tree" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-commit", @@ -3227,7 +3227,7 @@ dependencies = [ [[package]] name = "p3-poseidon2" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "gcd", "p3-field", @@ -3239,7 +3239,7 @@ dependencies = [ [[package]] name = "p3-symmetric" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-field", @@ -3249,7 +3249,7 @@ dependencies = [ [[package]] name = "p3-uni-stark" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-air", @@ -3267,7 +3267,7 @@ dependencies = [ [[package]] name = "p3-util" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "serde", ] @@ -4498,7 +4498,7 @@ dependencies = [ [[package]] name = "sp1-core" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "anyhow", "arrayref", @@ -4558,7 +4558,7 @@ dependencies = [ [[package]] name = "sp1-derive" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "proc-macro2", "quote", @@ -4568,7 +4568,7 @@ dependencies = [ [[package]] name = "sp1-primitives" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "itertools 0.13.0", "lazy_static", @@ -4581,7 +4581,7 @@ dependencies = [ [[package]] name = "sp1-prover" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "anyhow", "backtrace", @@ -4626,7 +4626,7 @@ dependencies = [ [[package]] name = "sp1-recursion-circuit" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "bincode", "itertools 0.13.0", @@ -4649,7 +4649,7 @@ dependencies = [ [[package]] name = "sp1-recursion-compiler" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "backtrace", "itertools 0.13.0", @@ -4673,7 +4673,7 @@ dependencies = [ [[package]] name = "sp1-recursion-core" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "arrayref", "backtrace", @@ -4707,7 +4707,7 @@ dependencies = [ [[package]] name = "sp1-recursion-derive" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "proc-macro2", "quote", @@ -4717,7 +4717,7 @@ dependencies = [ [[package]] name = "sp1-recursion-gnark-ffi" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "anyhow", "bincode", @@ -4742,7 +4742,7 @@ dependencies = [ [[package]] name = "sp1-recursion-program" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "itertools 0.13.0", "p3-air", @@ -4769,7 +4769,7 @@ dependencies = [ [[package]] name = "sp1-sdk" version = "0.1.0" -source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.7-testnet#20ab7791c3570cd5712c75098bcd8e8629b28d9b" +source = "git+https://github.com/succinctlabs/sp1?tag=v1.0.8-testnet#14eb569d41d24721ffbd407d6060e202482d659c" dependencies = [ "alloy-sol-types", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f37470b..40c4bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ path = "src/bin/artifacts.rs" anyhow = "1.0.86" dotenv = "0.15.0" log = "0.4.21" -sp1-sdk = { git = "https://github.com/succinctlabs/sp1", tag = "v1.0.7-testnet" } +sp1-sdk = { git = "https://github.com/succinctlabs/sp1", tag = "v1.0.8-testnet" } diff --git a/contracts/.env.example b/contracts/.env.example new file mode 100644 index 0000000..759017f --- /dev/null +++ b/contracts/.env.example @@ -0,0 +1,28 @@ +### The chains to deploy to +CHAIN_IDS= + +### RPCs for each chain +RPC_1= +RPC_11155111= +RPC_17000= +RPC_100= +RPC_137= +RPC_42161= +RPC_421614= +RPC_8453= +RPC_84532= +RPC_534352= +RPC_534351= + +# Etherscan API keys for each chain +ETHERSCAN_API_KEY_1= +ETHERSCAN_API_KEY_11155111= +ETHERSCAN_API_KEY_17000= +ETHERSCAN_API_KEY_100= +ETHERSCAN_API_KEY_137= +ETHERSCAN_API_KEY_42161= +ETHERSCAN_API_KEY_421614= +ETHERSCAN_API_KEY_8453= +ETHERSCAN_API_KEY_84532= +ETHERSCAN_API_KEY_534352= +ETHERSCAN_API_KEY_534351= \ No newline at end of file diff --git a/contracts/.gitignore b/contracts/.gitignore index 85198aa..e668838 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -2,13 +2,12 @@ cache/ out/ -# Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/**/dry-run/ +# Ignores broadcast logs +broadcast/ # Docs docs/ -# Dotenv file +# Dotenv files .env +.env.deployments \ No newline at end of file diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json new file mode 100644 index 0000000..a699be3 --- /dev/null +++ b/contracts/deployments/11155111.json @@ -0,0 +1,4 @@ +{ + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000003", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e" +} \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json new file mode 100644 index 0000000..a699be3 --- /dev/null +++ b/contracts/deployments/421614.json @@ -0,0 +1,4 @@ +{ + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000003", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e" +} \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 598af94..99de8e8 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -3,6 +3,9 @@ src = "src" out = "out" libs = ["lib"] solc = '0.8.20' +optimizer = true +optimizer_runs = 200 +fs_permissions = [{ access = "read-write", path = "./"}] [fmt] line_length = 100 @@ -10,4 +13,17 @@ tab_width = 4 func_attrs_with_params_multiline = true ignore = ["lib/**"] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +[etherscan] +ethereum = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } +sepolia = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } +holesky = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } +gnosis = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } +polygon = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } +arbitrum = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } +arbitrum-sepolia = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } +base = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } +base-sepolia = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } +scroll = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } +scroll-sepolia = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file diff --git a/contracts/script/SP1Verifier.s.sol b/contracts/script/SP1Verifier.s.sol deleted file mode 100644 index 6bf7f50..0000000 --- a/contracts/script/SP1Verifier.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import {Script, console} from "forge-std/Script.sol"; - -contract SP1VerifierScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/contracts/script/deploy/SP1Verifier.s.sol b/contracts/script/deploy/SP1Verifier.s.sol new file mode 100644 index 0000000..f79aea9 --- /dev/null +++ b/contracts/script/deploy/SP1Verifier.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {console} from "forge-std/console.sol"; +import {BaseScript} from "../utils/Base.s.sol"; +import {SP1Verifier} from "../../src/v1.0.7-testnet/SP1Verifier.sol"; +import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; + +contract SP1VerifierScript is BaseScript { + function run() external multichain broadcaster { + console.log("Deploying SP1_VERIFIER on chain %s", vm.toString(block.chainid)); + + // Read env variables + bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); + address SP1_VERIFIER_GATEWAY = readAddress("SP1_VERIFIER_GATEWAY"); + + // Deploy contract + address verifier = address(new SP1Verifier{salt: CREATE2_SALT}()); + + // Add the verifier to the gateway + SP1VerifierGateway gateway = SP1VerifierGateway(SP1_VERIFIER_GATEWAY); + gateway.addRoute(verifier); + + // Write address + writeAddress("SP1_VERIFIER", verifier); + } +} diff --git a/contracts/script/deploy/SP1VerifierGateway.sol b/contracts/script/deploy/SP1VerifierGateway.sol new file mode 100644 index 0000000..dcc2bc3 --- /dev/null +++ b/contracts/script/deploy/SP1VerifierGateway.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {console} from "forge-std/console.sol"; +import {BaseScript} from "../utils/Base.s.sol"; +import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; + +contract SP1VerifierGatewayScript is BaseScript { + function run() external multichain broadcaster { + console.log("Deploying SP1_VERIFIER_GATEWAY on chain %s", vm.toString(block.chainid)); + + // Read env variables + bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); + address OWNER = readAddress("OWNER"); + + // Deploy contract + address gateway = address(new SP1VerifierGateway{salt: CREATE2_SALT}(OWNER)); + + // Write address + writeAddress("SP1_VERIFIER_GATEWAY", gateway); + } +} diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol new file mode 100644 index 0000000..4649776 --- /dev/null +++ b/contracts/script/utils/Base.s.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Vm.sol"; +import "forge-std/console.sol"; +import {Script} from "forge-std/Script.sol"; +import {Strings} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; + +/// @notice Script to inherit from to get access to helper functions +abstract contract BaseScript is Script { + /// @notice Run the command with the `--broadcast` flag to send the transaction to the chain, + /// otherwise just simulate the transaction execution. + modifier broadcaster() { + vm.startBroadcast(msg.sender); + _; + vm.stopBroadcast(); + } + + /// @notice When used, runs the script on the chains specified in the `CHAIN_IDS` env variable. + /// Must have a `RPC_${CHAIN_ID}` env variable set for each chain. + modifier multichain() { + uint256[] memory chainIds = vm.envUint("CHAIN_IDS", ","); + for (uint256 i = 0; i < chainIds.length; i++) { + uint256 chainId = chainIds[i]; + + // Switch to the chain using the RPC + string memory rpc = vm.envString(string.concat("RPC_", vm.toString(chainId))); + vm.createSelectFork(rpc); + + // or try vm.createSelectFork(vm.rpcUrl(chainName)); + // https://github.com/ourzora/zora-protocol/blob/87fd4a0f06cbabe9bb081eb01babc7222bd1db91/packages/frames/script/MultichainScript.sol#L35 + + _; + } + } + + function deployments() public view returns (string memory) { + return vm.readFile(string.concat("./deployments/", vm.toString(block.chainid), ".json")); + } + + function readAddress(string memory name) internal view returns (address) { + return vm.parseJsonAddress(deployments(), name); + } + + function readBytes32(string memory name) internal view returns (bytes32) { + return vm.parseJsonBytes32(deployments(), name); + } + + // function writeAddress(string memory name) internal view returns (address) { + // return vm.writeJson(deployments(), name, vm.envAddress(name)); + // } + + function writeAddress(string memory name, address value) internal { + string memory directory = string.concat(vm.projectRoot(), deployments()); + if (!vm.exists(directory)) { + vm.createDir(directory, true); + } + + string memory file = + string.concat(vm.projectRoot(), deployments(), vm.toString(block.chainid), ".json"); + bool exists = vm.exists(file); + if (!exists) { + vm.writeFile(file, "{}"); + } + + string memory json = vm.readFile(file); + if (vm.keyExists(json, string.concat(".", name))) { + vm.writeJson(Strings.toHexString(value), file, string.concat(".", name)); + } else { + string memory root = "root"; + vm.serializeJson(root, json); + vm.writeJson(vm.serializeAddress(root, name, value), file); + } + } + + function writeEnvAddress(string memory file, string memory name, address value) internal { + string memory addrVar = string.concat(name, "_", vm.toString(block.chainid)); + vm.setEnv(addrVar, Strings.toHexString(value)); + vm.writeLine(file, string.concat(string.concat(addrVar, "="), Strings.toHexString(value))); + console.log(string.concat(string.concat(addrVar, "="), Strings.toHexString(value))); + } +} diff --git a/contracts/script/verify.sh b/contracts/script/verify.sh new file mode 100755 index 0000000..2cc7a58 --- /dev/null +++ b/contracts/script/verify.sh @@ -0,0 +1,71 @@ +USAGE="Usage: ./verify.sh \n Example: ./verify.sh \"SP1VerifierGateway\" \"5 420 84531 421613\" \"true\" \"$(cast abi-encode "constructor(address)" "0x6e4f1e9ea315ebfd69d18c2db974eef6105fb803")\"" + +if [ -z "$1" ]; then + echo $USAGE + exit 1 +fi + +if [ -z "$2" ]; then + echo $USAGE + exit 1 +fi + +if [[ -z "$3" || ( "$3" != "true" && "$3" != "false" ) ]]; then + echo "$USAGE" + exit 1 +fi + +CONTRACT=$1 +IFS=' ' read -r -a CHAIN_IDS <<< "$2" +IS_PROXY=$3 +CONSTRUCTOR_ARGS=$4 + +# Load environment variables from .env +source .env + +# Create .env.deployments if it doesn't exist +if [ ! -f .env.deployments ]; then + touch .env.deployments +fi + +for chain_id in "${CHAIN_IDS[@]}"; do + set -a + source .env.deployments + set +a + + contract=$CONTRACT + constructor_args=${CONSTRUCTOR_ARGS[$i]} + is_proxy=${IS_PROXY[$i]} + snake_case=$(echo $contract | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g' | tr '[:lower:]' '[:upper:]') + + address_var=$(echo "${snake_case}_${chain_id}") + address=$(echo $(eval echo "\$$address_var")) + + etherscan_key_var=$(echo 'ETHERSCAN_API_KEY_'"${chain_id}") + etherscan_key=$(echo $(eval echo "\$$etherscan_key_var")) + + # Proxy logic + if $is_proxy; then + rpc_url_var=$(echo 'RPC_'"${chain_id}") + rpc_url=$(echo $(eval echo "\$$rpc_url_var")) + # ERC1967Proxy implementation slot + impl=$(cast storage $address 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $rpc_url) + impl_addr=$(cast abi-decode "a()(address)" $impl) + proxy_constructor_calldata=$(cast abi-encode "constructor(address,bytes)" $impl_addr "$constructor_args") + echo "Verifying Proxy_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" + forge verify-contract $address src/upgrade/Proxy.sol:Proxy --chain ${chain_id} --watch --constructor-args $proxy_constructor_calldata --verifier etherscan --etherscan-api-key $etherscan_key + address=$impl_addr + constructor_args="" + fi + + echo "Verifying ${contract}_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" + if [ -n "$constructor_args" ]; then + forge verify-contract $address $contract --chain ${chain_id} --watch --constructor-args $constructor_args --verifier etherscan --etherscan-api-key $etherscan_key + else + forge verify-contract $address $contract --chain ${chain_id} --watch --verifier etherscan --etherscan-api-key $etherscan_key + fi +done + + +// maybe can be done in forge: https://github.com/ethereum-optimism/optimism/blob/6062bc14aa887cbbb186377103b99ae5f2cf76e6/packages/contracts-bedrock/scripts/Artifacts.s.sol#L232 +sepollia, arb-sepolia, base-sepolia \ No newline at end of file From 0e320db21607445de0735ad8bf7e62764db00667 Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 19 Jun 2024 20:07:32 -0700 Subject: [PATCH 02/13] add v1.0.8 --- contracts/script/deploy/SP1Verifier.s.sol | 2 +- contracts/src/SP1MockVerifier.sol | 10 +- .../src/v1.0.8-testnet/PlonkVerifier.sol | 1375 +++++++++++++++++ contracts/src/v1.0.8-testnet/SP1Verifier.sol | 51 + 4 files changed, 1436 insertions(+), 2 deletions(-) create mode 100644 contracts/src/v1.0.8-testnet/PlonkVerifier.sol create mode 100644 contracts/src/v1.0.8-testnet/SP1Verifier.sol diff --git a/contracts/script/deploy/SP1Verifier.s.sol b/contracts/script/deploy/SP1Verifier.s.sol index f79aea9..14ed7f3 100644 --- a/contracts/script/deploy/SP1Verifier.s.sol +++ b/contracts/script/deploy/SP1Verifier.s.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import {console} from "forge-std/console.sol"; import {BaseScript} from "../utils/Base.s.sol"; -import {SP1Verifier} from "../../src/v1.0.7-testnet/SP1Verifier.sol"; +import {SP1Verifier} from "../../src/v1.0.8-testnet/SP1Verifier.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; contract SP1VerifierScript is BaseScript { diff --git a/contracts/src/SP1MockVerifier.sol b/contracts/src/SP1MockVerifier.sol index b403d02..0ca6f4a 100644 --- a/contracts/src/SP1MockVerifier.sol +++ b/contracts/src/SP1MockVerifier.sol @@ -7,9 +7,17 @@ import {ISP1Verifier} from "./ISP1Verifier.sol"; /// @author Succinct Labs /// @notice This contracts implements a Mock solidity verifier for SP1. contract SP1MockVerifier is ISP1Verifier { + function VERSION() external pure returns (string memory) { + return "TODO"; + } + /// @notice Verifies a mock proof with given public values and vkey. /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. - function verifyProof(bytes32, bytes calldata, bytes calldata proofBytes) external pure { + function verifyProof( + bytes32, + bytes memory, + bytes memory proofBytes + ) external pure { assert(proofBytes.length == 0); } } diff --git a/contracts/src/v1.0.8-testnet/PlonkVerifier.sol b/contracts/src/v1.0.8-testnet/PlonkVerifier.sol new file mode 100644 index 0000000..7bf0041 --- /dev/null +++ b/contracts/src/v1.0.8-testnet/PlonkVerifier.sol @@ -0,0 +1,1375 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2023 Consensys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +pragma solidity ^0.8.20; + +contract PlonkVerifier { + uint256 private constant R_MOD = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 private constant R_MOD_MINUS_ONE = + 21888242871839275222246405745257275088548364400416034343698204186575808495616; + uint256 private constant P_MOD = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + uint256 private constant G2_SRS_0_X_0 = + 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 private constant G2_SRS_0_X_1 = + 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 private constant G2_SRS_0_Y_0 = + 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 private constant G2_SRS_0_Y_1 = + 8495653923123431417604973247489272438418190587263600148770280649306958101930; + + uint256 private constant G2_SRS_1_X_0 = + 15805639136721018565402881920352193254830339253282065586954346329754995870280; + uint256 private constant G2_SRS_1_X_1 = + 19089565590083334368588890253123139704298730990782503769911324779715431555531; + uint256 private constant G2_SRS_1_Y_0 = + 9779648407879205346559610309258181044130619080926897934572699915909528404984; + uint256 private constant G2_SRS_1_Y_1 = + 6779728121489434657638426458390319301070371227460768374343986326751507916979; + + uint256 private constant G1_SRS_X = + 14312776538779914388377568895031746459131577658076416373430523308756343304251; + uint256 private constant G1_SRS_Y = + 11763105256161367503191792604679297387056316997144156930871823008787082098465; + + // ----------------------- vk --------------------- + uint256 private constant VK_NB_PUBLIC_INPUTS = 2; + uint256 private constant VK_DOMAIN_SIZE = 67108864; + uint256 private constant VK_INV_DOMAIN_SIZE = + 21888242545679039938882419398440172875981108180010270949818755658014750055173; + uint256 private constant VK_OMEGA = + 7419588552507395652481651088034484897579724952953562618697845598160172257810; + uint256 private constant VK_QL_COM_X = + 14585543372410019003092143328009702539190296007679404836599959939969866838166; + uint256 private constant VK_QL_COM_Y = + 17722824277917999366546751581516521560604709882619404516618901119648408567644; + uint256 private constant VK_QR_COM_X = + 10898091116425734967678806357954804971805650318410260176362236603111593070946; + uint256 private constant VK_QR_COM_Y = + 2924038286053928979399281209882141482034634465096220233633728805460582456630; + uint256 private constant VK_QM_COM_X = + 2031048125815750610347533979665341648587796766028921135553237592603463331141; + uint256 private constant VK_QM_COM_Y = + 8155065622593766679535254109229393197593220466268704577946828046842494239503; + uint256 private constant VK_QO_COM_X = + 5947740093533960953767408143123646997500725355604419790528882338800927905402; + uint256 private constant VK_QO_COM_Y = + 16526622583078899807158771486725089129936075882133814996745054899696208585050; + uint256 private constant VK_QK_COM_X = + 119740621302450787001135488439015439349927647665033252743058387575629283276; + uint256 private constant VK_QK_COM_Y = + 13024371462468475459400360697703527331499004139216279341600910316411324286861; + + uint256 private constant VK_S1_COM_X = + 10627327753818917257580031743580923447218792977466576262416509126412843282369; + uint256 private constant VK_S1_COM_Y = + 18778204069629010711726992370860512366080746441266922837923685723716465874545; + + uint256 private constant VK_S2_COM_X = + 4133742905916748837494066704724945614644731246453049829981019280272507803398; + uint256 private constant VK_S2_COM_Y = + 6466467061148402844596596206135529025731346998429306278744231736980842648921; + + uint256 private constant VK_S3_COM_X = + 8901602721884560254777014558991324752661207925612502603937647267620946902478; + uint256 private constant VK_S3_COM_Y = + 2765992643976542253368180121378505606157970815510675049234608868732339659772; + + uint256 private constant VK_COSET_SHIFT = 5; + + uint256 private constant VK_QCP_0_X = + 12771699097177619500468137065083938574724179973377295854993624477225534222388; + uint256 private constant VK_QCP_0_Y = + 5757719256900570664017938361173180574878811923841521668906048492539193870622; + + uint256 private constant VK_INDEX_COMMIT_API_0 = 32440323; + uint256 private constant VK_NB_CUSTOM_GATES = 1; + + // ------------------------------------------------ + + // offset proof + + uint256 private constant PROOF_L_COM_X = 0x0; + uint256 private constant PROOF_L_COM_Y = 0x20; + uint256 private constant PROOF_R_COM_X = 0x40; + uint256 private constant PROOF_R_COM_Y = 0x60; + uint256 private constant PROOF_O_COM_X = 0x80; + uint256 private constant PROOF_O_COM_Y = 0xa0; + + // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2 + uint256 private constant PROOF_H_0_X = 0xc0; + uint256 private constant PROOF_H_0_Y = 0xe0; + uint256 private constant PROOF_H_1_X = 0x100; + uint256 private constant PROOF_H_1_Y = 0x120; + uint256 private constant PROOF_H_2_X = 0x140; + uint256 private constant PROOF_H_2_Y = 0x160; + + // wire values at zeta + uint256 private constant PROOF_L_AT_ZETA = 0x180; + uint256 private constant PROOF_R_AT_ZETA = 0x1a0; + uint256 private constant PROOF_O_AT_ZETA = 0x1c0; + + // S1(zeta),S2(zeta) + uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta) + uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta) + + // [Z] + uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_X = 0x220; + uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_Y = 0x240; + + uint256 private constant PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA = 0x260; // z(w*zeta) + + // Folded proof for the opening of linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x280; + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2a0; + + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x2c0; + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x2e0; + + uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x300; + uint256 private constant PROOF_BSB_COMMITMENTS = 0x320; + + // -> next part of proof is + // [ openings_selector_commits || commitments_wires_commit_api] + + // -------- offset state + + // challenges to check the claimed quotient + + uint256 private constant STATE_ALPHA = 0x0; + uint256 private constant STATE_BETA = 0x20; + uint256 private constant STATE_GAMMA = 0x40; + uint256 private constant STATE_ZETA = 0x60; + uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80; + uint256 private constant STATE_FOLDED_H_X = 0xa0; + uint256 private constant STATE_FOLDED_H_Y = 0xc0; + uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0; + uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100; + uint256 private constant STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA = 0x120; + uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x140; // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_X = 0x160; // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x180; + uint256 private constant STATE_PI = 0x1a0; + uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1c0; + uint256 private constant STATE_GAMMA_KZG = 0x1e0; + uint256 private constant STATE_SUCCESS = 0x200; + uint256 private constant STATE_CHECK_VAR = 0x220; // /!\ this slot is used for debugging only + uint256 private constant STATE_LAST_MEM = 0x240; + + // -------- utils (for Fiat Shamir) + uint256 private constant FS_ALPHA = 0x616C706861; // "alpha" + uint256 private constant FS_BETA = 0x62657461; // "beta" + uint256 private constant FS_GAMMA = 0x67616d6d61; // "gamma" + uint256 private constant FS_ZETA = 0x7a657461; // "zeta" + uint256 private constant FS_GAMMA_KZG = 0x67616d6d61; // "gamma" + + // -------- errors + uint256 private constant ERROR_STRING_ID = + 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + // -------- utils (for hash_fr) + uint256 private constant HASH_FR_BB = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant HASH_FR_ZERO_UINT256 = 0; + uint8 private constant HASH_FR_LEN_IN_BYTES = 48; + uint8 private constant HASH_FR_SIZE_DOMAIN = 11; + uint8 private constant HASH_FR_ONE = 1; + uint8 private constant HASH_FR_TWO = 2; + + // -------- precompiles + uint8 private constant MOD_EXP = 0x5; + uint8 private constant EC_ADD = 0x6; + uint8 private constant EC_MUL = 0x7; + uint8 private constant EC_PAIR = 0x8; + + /// Verify a Plonk proof. + /// Reverts if the proof or the public inputs are malformed. + /// @param proof serialised plonk proof (using gnark's MarshalSolidity) + /// @param public_inputs (must be reduced) + /// @return success true if the proof passes false otherwise + function Verify(bytes calldata proof, uint256[] calldata public_inputs) + public + view + returns (bool success) + { + assembly { + let mem := mload(0x40) + let freeMem := add(mem, STATE_LAST_MEM) + + // sanity checks + check_number_of_public_inputs(public_inputs.length) + check_inputs_size(public_inputs.length, public_inputs.offset) + check_proof_size(proof.length) + check_proof_openings_size(proof.offset) + + // compute the challenges + let prev_challenge_non_reduced + prev_challenge_non_reduced := + derive_gamma(proof.offset, public_inputs.length, public_inputs.offset) + prev_challenge_non_reduced := derive_beta(prev_challenge_non_reduced) + prev_challenge_non_reduced := derive_alpha(proof.offset, prev_challenge_non_reduced) + derive_zeta(proof.offset, prev_challenge_non_reduced) + + // evaluation of Z=Xⁿ-1 at ζ, we save this value + let zeta := mload(add(mem, STATE_ZETA)) + let zeta_power_n_minus_one := + addmod(pow(zeta, VK_DOMAIN_SIZE, freeMem), sub(R_MOD, 1), R_MOD) + mstore(add(mem, STATE_ZETA_POWER_N_MINUS_ONE), zeta_power_n_minus_one) + + // public inputs contribution + let l_pi := sum_pi_wo_api_commit(public_inputs.offset, public_inputs.length, freeMem) + let l_pi_commit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_pi_commit, l_pi, R_MOD) + mstore(add(mem, STATE_PI), l_pi) + + compute_alpha_square_lagrange_0() + verify_opening_linearised_polynomial(proof.offset) + fold_h(proof.offset) + compute_commitment_linearised_polynomial(proof.offset) + compute_gamma_kzg(proof.offset) + fold_state(proof.offset) + batch_verify_multi_points(proof.offset) + + success := mload(add(mem, STATE_SUCCESS)) + + // Beginning errors ------------------------------------------------- + + function error_nb_public_inputs() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x1d) + mstore(add(ptError, 0x44), "wrong number of public inputs") + revert(ptError, 0x64) + } + + /// Called when an operation on Bn254 fails + /// @dev for instance when calling EcMul on a point not on Bn254. + function error_ec_op() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x12) + mstore(add(ptError, 0x44), "error ec operation") + revert(ptError, 0x64) + } + + /// Called when one of the public inputs is not reduced. + function error_inputs_size() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x18) + mstore(add(ptError, 0x44), "inputs are bigger than r") + revert(ptError, 0x64) + } + + /// Called when the size proof is not as expected + /// @dev to avoid overflow attack for instance + function error_proof_size() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x10) + mstore(add(ptError, 0x44), "wrong proof size") + revert(ptError, 0x64) + } + + /// Called when one the openings is bigger than r + /// The openings are the claimed evalutions of a polynomial + /// in a Kzg proof. + function error_proof_openings_size() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x16) + mstore(add(ptError, 0x44), "openings bigger than r") + revert(ptError, 0x64) + } + + function error_pairing() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0xd) + mstore(add(ptError, 0x44), "error pairing") + revert(ptError, 0x64) + } + + function error_verify() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0xc) + mstore(add(ptError, 0x44), "error verify") + revert(ptError, 0x64) + } + + function error_random_generation() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x14) + mstore(add(ptError, 0x44), "error random gen kzg") + revert(ptError, 0x64) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + /// @param s actual number of public inputs + function check_number_of_public_inputs(s) { + if iszero(eq(s, VK_NB_PUBLIC_INPUTS)) { error_nb_public_inputs() } + } + + /// Checks that the public inputs are < R_MOD. + /// @param s number of public inputs + /// @param p pointer to the public inputs array + function check_inputs_size(s, p) { + for { let i } lt(i, s) { i := add(i, 1) } { + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_inputs_size() } + p := add(p, 0x20) + } + } + + /// Checks if the proof is of the correct size + /// @param actual_proof_size size of the proof (not the expected size) + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x300, mul(VK_NB_CUSTOM_GATES, 0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } + } + + /// Checks if the multiple openings of the polynomials are < R_MOD. + /// @param aproof pointer to the beginning of the proof + /// @dev the 'a' prepending proof is to have a local name + function check_proof_openings_size(aproof) { + // PROOF_L_AT_ZETA + let p := add(aproof, PROOF_L_AT_ZETA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_R_AT_ZETA + p := add(aproof, PROOF_R_AT_ZETA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_O_AT_ZETA + p := add(aproof, PROOF_O_AT_ZETA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_S1_AT_ZETA + p := add(aproof, PROOF_S1_AT_ZETA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_S2_AT_ZETA + p := add(aproof, PROOF_S2_AT_ZETA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA + p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA) + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + + // PROOF_OPENING_QCP_AT_ZETA + + p := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + for { let i := 0 } lt(i, VK_NB_CUSTOM_GATES) { i := add(i, 1) } { + if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } + p := add(p, 0x20) + } + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + /// Derive gamma as Sha256() + /// @param aproof pointer to the proof + /// @param nb_pi number of public inputs + /// @param pi pointer to the array of public inputs + /// @return the challenge gamma, not reduced + /// @notice The transcript is the concatenation (in this order) of: + /// * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256. + /// * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points + /// * the commitments of Ql, Qr, Qm, Qo, Qk + /// * the public inputs + /// * the commitments of the wires related to the custom gates (commitments_wires_commit_api) + /// * commitments to L, R, O (proof__com_) + /// The data described above is written starting at mPtr. "gamma" lies on 5 bytes, + /// and is encoded as a uint256 number n. In basis b = 256, the number looks like this + /// [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b + /// Gamma reduced (the actual challenge) is stored at add(state, state_gamma) + function derive_gamma(aproof, nb_pi, pi) -> gamma_not_reduced { + let state := mload(0x40) + let mPtr := add(state, STATE_LAST_MEM) + + // gamma + // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] + // (same for alpha, beta, zeta) + mstore(mPtr, FS_GAMMA) // "gamma" + + mstore(add(mPtr, 0x20), VK_S1_COM_X) + mstore(add(mPtr, 0x40), VK_S1_COM_Y) + mstore(add(mPtr, 0x60), VK_S2_COM_X) + mstore(add(mPtr, 0x80), VK_S2_COM_Y) + mstore(add(mPtr, 0xa0), VK_S3_COM_X) + mstore(add(mPtr, 0xc0), VK_S3_COM_Y) + mstore(add(mPtr, 0xe0), VK_QL_COM_X) + mstore(add(mPtr, 0x100), VK_QL_COM_Y) + mstore(add(mPtr, 0x120), VK_QR_COM_X) + mstore(add(mPtr, 0x140), VK_QR_COM_Y) + mstore(add(mPtr, 0x160), VK_QM_COM_X) + mstore(add(mPtr, 0x180), VK_QM_COM_Y) + mstore(add(mPtr, 0x1a0), VK_QO_COM_X) + mstore(add(mPtr, 0x1c0), VK_QO_COM_Y) + mstore(add(mPtr, 0x1e0), VK_QK_COM_X) + mstore(add(mPtr, 0x200), VK_QK_COM_Y) + + mstore(add(mPtr, 0x220), VK_QCP_0_X) + mstore(add(mPtr, 0x240), VK_QCP_0_Y) + + // public inputs + let _mPtr := add(mPtr, 0x260) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // commitments to l, r, o + let size_commitments_lro_in_bytes := 0xc0 + calldatacopy(_mPtr, aproof, size_commitments_lro_in_bytes) + _mPtr := add(_mPtr, size_commitments_lro_in_bytes) + + // total size is : + // sizegamma(=0x5) + 11*64(=0x2c0) + // + nb_public_inputs*0x20 + // + nb_custom gates*0x40 + let size := add(0x2c5, size_pi_in_bytes) + + size := add(size, mul(VK_NB_CUSTOM_GATES, 0x40)) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { error_verify() } + gamma_not_reduced := mload(mPtr) + mstore(add(state, STATE_GAMMA), mod(gamma_not_reduced, R_MOD)) + } + + /// derive beta as Sha256 + /// @param gamma_not_reduced the previous challenge (gamma) not reduced + /// @return beta_not_reduced the next challenge, beta, not reduced + /// @notice the transcript consists of the previous challenge only. + /// The reduced version of beta is stored at add(state, state_beta) + function derive_beta(gamma_not_reduced) -> beta_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + + // beta + mstore(mPtr, FS_BETA) // "beta" + mstore(add(mPtr, 0x20), gamma_not_reduced) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { error_verify() } + beta_not_reduced := mload(mPtr) + mstore(add(state, STATE_BETA), mod(beta_not_reduced, R_MOD)) + } + + /// derive alpha as sha256 + /// @param aproof pointer to the proof object + /// @param beta_not_reduced the previous challenge (beta) not reduced + /// @return alpha_not_reduced the next challenge, alpha, not reduced + /// @notice the transcript consists of the previous challenge (beta) + /// not reduced, the commitments to the wires associated to the QCP_i, + /// and the commitment to the grand product polynomial + function derive_alpha(aproof, beta_not_reduced) -> alpha_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + let full_size := 0x65 // size("alpha") + 0x20 (previous challenge) + + // alpha + mstore(mPtr, FS_ALPHA) // "alpha" + let _mPtr := add(mPtr, 0x20) + mstore(_mPtr, beta_not_reduced) + _mPtr := add(_mPtr, 0x20) + + // Bsb22Commitments + let proof_bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS) + let size_bsb_commitments := mul(0x40, VK_NB_CUSTOM_GATES) + calldatacopy(_mPtr, proof_bsb_commitments, size_bsb_commitments) + _mPtr := add(_mPtr, size_bsb_commitments) + full_size := add(full_size, size_bsb_commitments) + + // [Z], the commitment to the grand product polynomial + calldatacopy(_mPtr, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), full_size, mPtr, 0x20) + if iszero(l_success) { error_verify() } + + alpha_not_reduced := mload(mPtr) + mstore(add(state, STATE_ALPHA), mod(alpha_not_reduced, R_MOD)) + } + + /// derive zeta as sha256 + /// @param aproof pointer to the proof object + /// @param alpha_not_reduced the previous challenge (alpha) not reduced + /// The transcript consists of the previous challenge and the commitment to + /// the quotient polynomial h. + function derive_zeta(aproof, alpha_not_reduced) { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + + // zeta + mstore(mPtr, FS_ZETA) // "zeta" + mstore(add(mPtr, 0x20), alpha_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, PROOF_H_0_X), 0xc0) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) + if iszero(l_success) { error_verify() } + let zeta_not_reduced := mload(mPtr) + mstore(add(state, STATE_ZETA), mod(zeta_not_reduced, R_MOD)) + } + // END challenges ------------------------------------------------- + + // BEGINNING compute_pi ------------------------------------------------- + + /// sum_pi_wo_api_commit computes the public inputs contributions, + /// except for the public inputs coming from the custom gate + /// @param ins pointer to the public inputs + /// @param n number of public inputs + /// @param mPtr free memory + /// @return pi_wo_commit public inputs contribution (except the public inputs coming from the custom gate) + function sum_pi_wo_api_commit(ins, n, mPtr) -> pi_wo_commit { + let state := mload(0x40) + let z := mload(add(state, STATE_ZETA)) + let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)) + + let li := mPtr + batch_compute_lagranges_at_z(z, zpnmo, n, li) + + let tmp := 0 + for { let i := 0 } lt(i, n) { i := add(i, 1) } { + tmp := mulmod(mload(li), calldataload(ins), R_MOD) + pi_wo_commit := addmod(pi_wo_commit, tmp, R_MOD) + li := add(li, 0x20) + ins := add(ins, 0x20) + } + } + + /// batch_compute_lagranges_at_z computes [L_0(z), .., L_{n-1}(z)] + /// @param z point at which the Lagranges are evaluated + /// @param zpnmo ζⁿ-1 + /// @param n number of public inputs (number of Lagranges to compute) + /// @param mPtr pointer to which the results are stored + function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { + let zn := mulmod(zpnmo, VK_INV_DOMAIN_SIZE, R_MOD) // 1/n * (ζⁿ - 1) + + let _w := 1 + let _mPtr := mPtr + for { let i := 0 } lt(i, n) { i := add(i, 1) } { + mstore(_mPtr, addmod(z, sub(R_MOD, _w), R_MOD)) + _w := mulmod(_w, VK_OMEGA, R_MOD) + _mPtr := add(_mPtr, 0x20) + } + batch_invert(mPtr, n, _mPtr) + _mPtr := mPtr + _w := 1 + for { let i := 0 } lt(i, n) { i := add(i, 1) } { + mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn, R_MOD), _w, R_MOD)) + _mPtr := add(_mPtr, 0x20) + _w := mulmod(_w, VK_OMEGA, R_MOD) + } + } + + /// @notice Montgomery trick for batch inversion mod R_MOD + /// @param ins pointer to the data to batch invert + /// @param number of elements to batch invert + /// @param mPtr free memory + function batch_invert(ins, nb_ins, mPtr) { + mstore(mPtr, 1) + let offset := 0 + for { let i := 0 } lt(i, nb_ins) { i := add(i, 1) } { + let prev := mload(add(mPtr, offset)) + let cur := mload(add(ins, offset)) + cur := mulmod(prev, cur, R_MOD) + offset := add(offset, 0x20) + mstore(add(mPtr, offset), cur) + } + ins := add(ins, sub(offset, 0x20)) + mPtr := add(mPtr, offset) + let inv := pow(mload(mPtr), sub(R_MOD, 2), add(mPtr, 0x20)) + for { let i := 0 } lt(i, nb_ins) { i := add(i, 1) } { + mPtr := sub(mPtr, 0x20) + let tmp := mload(ins) + let cur := mulmod(inv, mload(mPtr), R_MOD) + mstore(ins, cur) + inv := mulmod(inv, tmp, R_MOD) + ins := sub(ins, 0x20) + } + } + + /// Public inputs (the ones coming from the custom gate) contribution + /// @param aproof pointer to the proof + /// @param nb_public_inputs number of public inputs + /// @param mPtr pointer to free memory + /// @return pi_commit custom gate public inputs contribution + function sum_pi_commit(aproof, nb_public_inputs, mPtr) -> pi_commit { + let state := mload(0x40) + let z := mload(add(state, STATE_ZETA)) + let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)) + + let p := add(aproof, PROOF_BSB_COMMITMENTS) + + let h_fr, ith_lagrange + + h_fr := hash_fr(calldataload(p), calldataload(add(p, 0x20)), mPtr) + ith_lagrange := + compute_ith_lagrange_at_z( + z, zpnmo, add(nb_public_inputs, VK_INDEX_COMMIT_API_0), mPtr + ) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, R_MOD), R_MOD) + p := add(p, 0x40) + } + + /// Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + /// @param z zeta + /// @param zpmno ζⁿ-1 + /// @param i i-th lagrange + /// @param mPtr free memory + /// @return res = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) + function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr) -> res { + let w := pow(VK_OMEGA, i, mPtr) // w**i + i := addmod(z, sub(R_MOD, w), R_MOD) // z-w**i + w := mulmod(w, VK_INV_DOMAIN_SIZE, R_MOD) // w**i/n + i := pow(i, sub(R_MOD, 2), mPtr) // (z-w**i)**-1 + w := mulmod(w, i, R_MOD) // w**i/n*(z-w)**-1 + res := mulmod(w, zpnmo, R_MOD) + } + + /// @dev https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2 + /// @param x x coordinate of a point on Bn254(𝔽_p) + /// @param y y coordinate of a point on Bn254(𝔽_p) + /// @param mPtr free memory + /// @return res an element mod R_MOD + function hash_fr(x, y, mPtr) -> res { + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, HASH_FR_SIZE_DOMAIN] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, HASH_FR_ZERO_UINT256) + mstore(add(mPtr, 0x20), HASH_FR_ZERO_UINT256) + + // msg = x || y , both on 32 bytes + mstore(add(mPtr, 0x40), x) + mstore(add(mPtr, 0x60), y) + + // 0 || 48 || 0 all on 1 byte + mstore8(add(mPtr, 0x80), 0) + mstore8(add(mPtr, 0x81), HASH_FR_LEN_IN_BYTES) + mstore8(add(mPtr, 0x82), 0) + + // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] + mstore8(add(mPtr, 0x83), 0x42) + mstore8(add(mPtr, 0x84), 0x53) + mstore8(add(mPtr, 0x85), 0x42) + mstore8(add(mPtr, 0x86), 0x32) + mstore8(add(mPtr, 0x87), 0x32) + mstore8(add(mPtr, 0x88), 0x2d) + mstore8(add(mPtr, 0x89), 0x50) + mstore8(add(mPtr, 0x8a), 0x6c) + mstore8(add(mPtr, 0x8b), 0x6f) + mstore8(add(mPtr, 0x8c), 0x6e) + mstore8(add(mPtr, 0x8d), 0x6b) + + // size domain + mstore8(add(mPtr, 0x8e), HASH_FR_SIZE_DOMAIN) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { error_verify() } + + let b0 := mload(mPtr) + + // [b0 || one || dst || HASH_FR_SIZE_DOMAIN] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), HASH_FR_ONE) // 1 + + mstore8(add(mPtr, 0x21), 0x42) // dst + mstore8(add(mPtr, 0x22), 0x53) + mstore8(add(mPtr, 0x23), 0x42) + mstore8(add(mPtr, 0x24), 0x32) + mstore8(add(mPtr, 0x25), 0x32) + mstore8(add(mPtr, 0x26), 0x2d) + mstore8(add(mPtr, 0x27), 0x50) + mstore8(add(mPtr, 0x28), 0x6c) + mstore8(add(mPtr, 0x29), 0x6f) + mstore8(add(mPtr, 0x2a), 0x6e) + mstore8(add(mPtr, 0x2b), 0x6b) + + mstore8(add(mPtr, 0x2c), HASH_FR_SIZE_DOMAIN) // size domain + l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + if iszero(l_success) { error_verify() } + + // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) + + // [b0^b1 || two || dst || HASH_FR_SIZE_DOMAIN] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), HASH_FR_TWO) + + mstore8(add(mPtr, 0x41), 0x42) // dst + mstore8(add(mPtr, 0x42), 0x53) + mstore8(add(mPtr, 0x43), 0x42) + mstore8(add(mPtr, 0x44), 0x32) + mstore8(add(mPtr, 0x45), 0x32) + mstore8(add(mPtr, 0x46), 0x2d) + mstore8(add(mPtr, 0x47), 0x50) + mstore8(add(mPtr, 0x48), 0x6c) + mstore8(add(mPtr, 0x49), 0x6f) + mstore8(add(mPtr, 0x4a), 0x6e) + mstore8(add(mPtr, 0x4b), 0x6b) + + mstore8(add(mPtr, 0x4c), HASH_FR_SIZE_DOMAIN) // size domain + + let offset := add(mPtr, 0x20) + l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + if iszero(l_success) { error_verify() } + + // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. + // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) + // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + res := mulmod(mload(mPtr), HASH_FR_BB, R_MOD) // <- res = 2**128 * mPtr[:32] + let b1 := shr(128, mload(add(mPtr, 0x20))) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, R_MOD) + } + + // END compute_pi ------------------------------------------------- + + /// @notice compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where + /// * α = challenge derived in derive_gamma_beta_alpha_zeta + /// * n = vk_domain_size + /// * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + /// * ζ = zeta (challenge derived with Fiat Shamir) + function compute_alpha_square_lagrange_0() { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + + let res := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)) + let den := addmod(mload(add(state, STATE_ZETA)), sub(R_MOD, 1), R_MOD) + den := pow(den, sub(R_MOD, 2), mPtr) + den := mulmod(den, VK_INV_DOMAIN_SIZE, R_MOD) + res := mulmod(den, res, R_MOD) + + let l_alpha := mload(add(state, STATE_ALPHA)) + res := mulmod(res, l_alpha, R_MOD) + res := mulmod(res, l_alpha, R_MOD) + mstore(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0), res) + } + + /// @notice follows alg. p.13 of https://eprint.iacr.org/2019/953.pdf + /// with t₁ = t₂ = 1, and the proofs are ([digest] + [quotient] +purported evaluation): + /// * [state_folded_state_digests], [proof_batch_opening_at_zeta_x], state_folded_evals + /// * [proof_grand_product_commitment], [proof_opening_at_zeta_omega_x], [proof_grand_product_at_zeta_omega] + /// @param aproof pointer to the proof + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, STATE_LAST_MEM) + + // derive a random number. As there is no random generator, we + // do an FS like challenge derivation, depending on both digests and + // ζ to ensure that the prover cannot control the random numger. + // Note: adding the other point ζω is not needed, as ω is known beforehand. + mstore(mPtr, mload(add(state, STATE_FOLDED_DIGESTS_X))) + mstore(add(mPtr, 0x20), mload(add(state, STATE_FOLDED_DIGESTS_Y))) + mstore(add(mPtr, 0x40), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X))) + mstore(add(mPtr, 0x60), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y))) + mstore(add(mPtr, 0x80), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X))) + mstore(add(mPtr, 0xa0), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y))) + mstore(add(mPtr, 0xc0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X))) + mstore(add(mPtr, 0xe0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_Y))) + mstore(add(mPtr, 0x100), mload(add(state, STATE_ZETA))) + mstore(add(mPtr, 0x120), mload(add(state, STATE_GAMMA_KZG))) + let random := staticcall(gas(), 0x2, mPtr, 0x140, mPtr, 0x20) + if iszero(random) { error_random_generation() } + random := mod(mload(mPtr), R_MOD) // use the same variable as we are one variable away from getting stack-too-deep error... + + let folded_quotients := mPtr + mPtr := add(folded_quotients, 0x40) + mstore(folded_quotients, calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X))) + mstore( + add(folded_quotients, 0x20), + calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y)) + ) + point_acc_mul_calldata( + folded_quotients, add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), random, mPtr + ) + + let folded_digests := add(state, STATE_FOLDED_DIGESTS_X) + point_acc_mul_calldata( + folded_digests, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), random, mPtr + ) + + let folded_evals := add(state, STATE_FOLDED_CLAIMED_VALUES) + fr_acc_mul_calldata( + folded_evals, add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA), random + ) + + let folded_evals_commit := mPtr + mPtr := add(folded_evals_commit, 0x40) + mstore(folded_evals_commit, G1_SRS_X) + mstore(add(folded_evals_commit, 0x20), G1_SRS_Y) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := + staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if iszero(check_staticcall) { error_verify() } + + let folded_evals_commit_y := add(folded_evals_commit, 0x20) + mstore(folded_evals_commit_y, sub(P_MOD, mload(folded_evals_commit_y))) + point_add(folded_digests, folded_digests, folded_evals_commit, mPtr) + + let folded_points_quotients := mPtr + mPtr := add(mPtr, 0x40) + point_mul_calldata( + folded_points_quotients, + add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X), + mload(add(state, STATE_ZETA)), + mPtr + ) + let zeta_omega := mulmod(mload(add(state, STATE_ZETA)), VK_OMEGA, R_MOD) + random := mulmod(random, zeta_omega, R_MOD) + point_acc_mul_calldata( + folded_points_quotients, + add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), + random, + mPtr + ) + + point_add(folded_digests, folded_digests, folded_points_quotients, mPtr) + + let folded_quotients_y := add(folded_quotients, 0x20) + mstore(folded_quotients_y, sub(P_MOD, mload(folded_quotients_y))) + + mstore(mPtr, mload(folded_digests)) + mstore(add(mPtr, 0x20), mload(add(folded_digests, 0x20))) + mstore(add(mPtr, 0x40), G2_SRS_0_X_0) // the 4 lines are the canonical G2 point on BN254 + mstore(add(mPtr, 0x60), G2_SRS_0_X_1) + mstore(add(mPtr, 0x80), G2_SRS_0_Y_0) + mstore(add(mPtr, 0xa0), G2_SRS_0_Y_1) + mstore(add(mPtr, 0xc0), mload(folded_quotients)) + mstore(add(mPtr, 0xe0), mload(add(folded_quotients, 0x20))) + mstore(add(mPtr, 0x100), G2_SRS_1_X_0) + mstore(add(mPtr, 0x120), G2_SRS_1_X_1) + mstore(add(mPtr, 0x140), G2_SRS_1_Y_0) + mstore(add(mPtr, 0x160), G2_SRS_1_Y_1) + check_pairing_kzg(mPtr) + } + + /// @notice check_pairing_kzg checks the result of the final pairing product of the batched + /// kzg verification. The purpose of this function is to avoid exhausting the stack + /// in the function batch_verify_multi_points. + /// @param mPtr pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + if iszero(l_success) { error_pairing() } + let res_pairing := mload(0x00) + mstore(add(state, STATE_SUCCESS), res_pairing) + } + + /// @notice Fold the opening proofs at ζ: + /// * at state+state_folded_digest we store: [Linearised_polynomial]+γ[L] + γ²[R] + γ³[O] + γ⁴[S₁] +γ⁵[S₂] + ∑ᵢγ⁵⁺ⁱ[Pi_{i}] + /// * at state+state_folded_claimed_values we store: H(ζ) + γLinearised_polynomial(ζ)+γ²L(ζ) + γ³R(ζ)+ γ⁴O(ζ) + γ⁵S₁(ζ) +γ⁶S₂(ζ) + ∑ᵢγ⁶⁺ⁱPi_{i}(ζ) + /// @param aproof pointer to the proof + /// acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + let mPtr20 := add(mPtr, 0x20) + let mPtr40 := add(mPtr, 0x40) + + let l_gamma_kzg := mload(add(state, STATE_GAMMA_KZG)) + let acc_gamma := l_gamma_kzg + let state_folded_digests := add(state, STATE_FOLDED_DIGESTS_X) + + mstore( + add(state, STATE_FOLDED_DIGESTS_X), + mload(add(state, STATE_LINEARISED_POLYNOMIAL_X)) + ) + mstore( + add(state, STATE_FOLDED_DIGESTS_Y), + mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y)) + ) + mstore( + add(state, STATE_FOLDED_CLAIMED_VALUES), + mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA)) + ) + + point_acc_mul_calldata( + add(state, STATE_FOLDED_DIGESTS_X), add(aproof, PROOF_L_COM_X), acc_gamma, mPtr + ) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_L_AT_ZETA), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + point_acc_mul_calldata( + state_folded_digests, add(aproof, PROOF_R_COM_X), acc_gamma, mPtr + ) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_R_AT_ZETA), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + point_acc_mul_calldata( + state_folded_digests, add(aproof, PROOF_O_COM_X), acc_gamma, mPtr + ) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_O_AT_ZETA), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + mstore(mPtr, VK_S1_COM_X) + mstore(mPtr20, VK_S1_COM_Y) + point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), + add(aproof, PROOF_S1_AT_ZETA), + acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + mstore(mPtr, VK_S2_COM_X) + mstore(mPtr20, VK_S2_COM_Y) + point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), + add(aproof, PROOF_S2_AT_ZETA), + acc_gamma + ) + let poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + mstore(mPtr, VK_QCP_0_X) + mstore(mPtr20, VK_QCP_0_Y) + point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40) + fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), poqaz, acc_gamma) + poqaz := add(poqaz, 0x20) + } + + /// @notice generate the challenge (using Fiat Shamir) to fold the opening proofs + /// at ζ. + /// The process for deriving γ is the same as in derive_gamma but this time the inputs are + /// in this order (the [] means it's a commitment): + /// * ζ + /// * [Linearised polynomial] + /// * [L], [R], [O] + /// * [S₁] [S₂] + /// * [Pi_{i}] (wires associated to custom gates) + /// Then there are the purported evaluations of the previous committed polynomials: + /// * Linearised_polynomial(ζ) + /// * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + /// * Pi_{i}(ζ) + /// * Z(ζω) + /// @param aproof pointer to the proof + function compute_gamma_kzg(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + mstore(mPtr, FS_GAMMA_KZG) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA))) + mstore(add(mPtr, 0x40), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) + mstore(add(mPtr, 0x60), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) + calldatacopy(add(mPtr, 0x80), add(aproof, PROOF_L_COM_X), 0xc0) + mstore(add(mPtr, 0x140), VK_S1_COM_X) + mstore(add(mPtr, 0x160), VK_S1_COM_Y) + mstore(add(mPtr, 0x180), VK_S2_COM_X) + mstore(add(mPtr, 0x1a0), VK_S2_COM_Y) + + let offset := 0x1c0 + + mstore(add(mPtr, offset), VK_QCP_0_X) + mstore(add(mPtr, add(offset, 0x20)), VK_QCP_0_Y) + offset := add(offset, 0x40) + mstore( + add(mPtr, offset), mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA)) + ) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_L_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_R_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_O_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) + + let _mPtr := add(mPtr, add(offset, 0xc0)) + + let _poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + calldatacopy(_mPtr, _poqaz, mul(VK_NB_CUSTOM_GATES, 0x20)) + _mPtr := add(_mPtr, mul(VK_NB_CUSTOM_GATES, 0x20)) + + mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x14, mul(VK_NB_CUSTOM_GATES, 3)) // number of 32bytes elmts = 0x17 (zeta+3*6 for the digests+openings) + 3*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + 1 (opening of Z at ζω) + size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma + let check_staticcall := + staticcall( + gas(), + 0x2, + add(mPtr, start_input), + size_input, + add(state, STATE_GAMMA_KZG), + 0x20 + ) + if iszero(check_staticcall) { error_verify() } + mstore(add(state, STATE_GAMMA_KZG), mod(mload(add(state, STATE_GAMMA_KZG)), R_MOD)) + } + + function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) { + let state := mload(0x40) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + + mstore(mPtr, VK_QL_COM_X) + mstore(add(mPtr, 0x20), VK_QL_COM_Y) + point_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(add(aproof, PROOF_L_AT_ZETA)), + add(mPtr, 0x40) + ) + + mstore(mPtr, VK_QR_COM_X) + mstore(add(mPtr, 0x20), VK_QR_COM_Y) + point_acc_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(add(aproof, PROOF_R_AT_ZETA)), + add(mPtr, 0x40) + ) + + let rl := + mulmod( + calldataload(add(aproof, PROOF_L_AT_ZETA)), + calldataload(add(aproof, PROOF_R_AT_ZETA)), + R_MOD + ) + mstore(mPtr, VK_QM_COM_X) + mstore(add(mPtr, 0x20), VK_QM_COM_Y) + point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, rl, add(mPtr, 0x40)) + + mstore(mPtr, VK_QO_COM_X) + mstore(add(mPtr, 0x20), VK_QO_COM_Y) + point_acc_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(add(aproof, PROOF_O_AT_ZETA)), + add(mPtr, 0x40) + ) + + mstore(mPtr, VK_QK_COM_X) + mstore(add(mPtr, 0x20), VK_QK_COM_Y) + point_add( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + add(mPtr, 0x40) + ) + + let qcp_opening_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + let bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS) + for { let i := 0 } lt(i, VK_NB_CUSTOM_GATES) { i := add(i, 1) } { + mstore(mPtr, calldataload(bsb_commitments)) + mstore(add(mPtr, 0x20), calldataload(add(bsb_commitments, 0x20))) + point_acc_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(qcp_opening_at_zeta), + add(mPtr, 0x40) + ) + qcp_opening_at_zeta := add(qcp_opening_at_zeta, 0x20) + bsb_commitments := add(bsb_commitments, 0x40) + } + + mstore(mPtr, VK_S3_COM_X) + mstore(add(mPtr, 0x20), VK_S3_COM_Y) + point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s1, add(mPtr, 0x40)) + + mstore(mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X))) + mstore(add(mPtr, 0x20), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y))) + point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s2, add(mPtr, 0x40)) + + point_add( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + add(state, STATE_LINEARISED_POLYNOMIAL_X), + add(state, STATE_FOLDED_H_X), + mPtr + ) + } + + /// @notice Compute the commitment to the linearized polynomial equal to + /// L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] + + /// α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2}(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) + + /// α²*L₁(ζ)[Z] - Z_{H}(ζ)*(([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾*[H₂]) + /// where + /// * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id + /// * the [] means that it's a commitment (i.e. a point on Bn254(F_p)) + /// * Z_{H}(ζ) = ζ^n-1 + /// @param aproof pointer to the proof + function compute_commitment_linearised_polynomial(aproof) { + let state := mload(0x40) + let l_beta := mload(add(state, STATE_BETA)) + let l_gamma := mload(add(state, STATE_GAMMA)) + let l_zeta := mload(add(state, STATE_ZETA)) + let l_alpha := mload(add(state, STATE_ALPHA)) + + let u := + mulmod(calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), l_beta, R_MOD) + let v := mulmod(l_beta, calldataload(add(aproof, PROOF_S1_AT_ZETA)), R_MOD) + v := addmod(v, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD) + v := addmod(v, l_gamma, R_MOD) + + let w := mulmod(l_beta, calldataload(add(aproof, PROOF_S2_AT_ZETA)), R_MOD) + w := addmod(w, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD) + w := addmod(w, l_gamma, R_MOD) + + let s1 := mulmod(u, v, R_MOD) + s1 := mulmod(s1, w, R_MOD) + s1 := mulmod(s1, l_alpha, R_MOD) + + let coset_square := mulmod(VK_COSET_SHIFT, VK_COSET_SHIFT, R_MOD) + let betazeta := mulmod(l_beta, l_zeta, R_MOD) + u := addmod(betazeta, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD) + u := addmod(u, l_gamma, R_MOD) + + v := mulmod(betazeta, VK_COSET_SHIFT, R_MOD) + v := addmod(v, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD) + v := addmod(v, l_gamma, R_MOD) + + w := mulmod(betazeta, coset_square, R_MOD) + w := addmod(w, calldataload(add(aproof, PROOF_O_AT_ZETA)), R_MOD) + w := addmod(w, l_gamma, R_MOD) + + let s2 := mulmod(u, v, R_MOD) + s2 := mulmod(s2, w, R_MOD) + s2 := sub(R_MOD, s2) + s2 := mulmod(s2, l_alpha, R_MOD) + s2 := addmod(s2, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0)), R_MOD) + + // at this stage: + // * s₁ = α*Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β + // * s₂ = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + α²*L₁(ζ) + + compute_commitment_linearised_polynomial_ec(aproof, s1, s2) + } + + /// @notice compute -z_h(ζ)*([H₁] + ζᵐ⁺²[H₂] + ζ²⁽ᵐ⁺²⁾[H₃]) and store the result at + /// state + state_folded_h + /// @param aproof pointer to the proof + function fold_h(aproof) { + let state := mload(0x40) + let n_plus_two := add(VK_DOMAIN_SIZE, 2) + let mPtr := add(mload(0x40), STATE_LAST_MEM) + let zeta_power_n_plus_two := pow(mload(add(state, STATE_ZETA)), n_plus_two, mPtr) + point_mul_calldata( + add(state, STATE_FOLDED_H_X), + add(aproof, PROOF_H_2_X), + zeta_power_n_plus_two, + mPtr + ) + point_add_calldata( + add(state, STATE_FOLDED_H_X), + add(state, STATE_FOLDED_H_X), + add(aproof, PROOF_H_1_X), + mPtr + ) + point_mul( + add(state, STATE_FOLDED_H_X), + add(state, STATE_FOLDED_H_X), + zeta_power_n_plus_two, + mPtr + ) + point_add_calldata( + add(state, STATE_FOLDED_H_X), + add(state, STATE_FOLDED_H_X), + add(aproof, PROOF_H_0_X), + mPtr + ) + point_mul( + add(state, STATE_FOLDED_H_X), + add(state, STATE_FOLDED_H_X), + mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)), + mPtr + ) + let folded_h_y := mload(add(state, STATE_FOLDED_H_Y)) + folded_h_y := sub(P_MOD, folded_h_y) + mstore(add(state, STATE_FOLDED_H_Y), folded_h_y) + } + + /// @notice check that the opening of the linearised polynomial at zeta is equal to + /// - [ PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ) ] + /// @param aproof pointer to the proof + function verify_opening_linearised_polynomial(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 + s1 := + mulmod( + calldataload(add(aproof, PROOF_S1_AT_ZETA)), + mload(add(state, STATE_BETA)), + R_MOD + ) + s1 := addmod(s1, mload(add(state, STATE_GAMMA)), R_MOD) + s1 := addmod(s1, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 + s2 := + mulmod( + calldataload(add(aproof, PROOF_S2_AT_ZETA)), + mload(add(state, STATE_BETA)), + R_MOD + ) + s2 := addmod(s2, mload(add(state, STATE_GAMMA)), R_MOD) + s2 := addmod(s2, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD) + + // (o(ζ)+γ) + let o + o := + addmod( + calldataload(add(aproof, PROOF_O_AT_ZETA)), + mload(add(state, STATE_GAMMA)), + R_MOD + ) + + // α*Z(μζ)*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + s1 := mulmod(s1, s2, R_MOD) + s1 := mulmod(s1, o, R_MOD) + s1 := mulmod(s1, mload(add(state, STATE_ALPHA)), R_MOD) + s1 := + mulmod(s1, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD) + + // PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ) + s1 := addmod(s1, mload(add(state, STATE_PI)), R_MOD) + s2 := mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0)) + s2 := sub(R_MOD, s2) + s1 := addmod(s1, s2, R_MOD) + s1 := sub(R_MOD, s1) + + mstore(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA), s1) + } + + // BEGINNING utils math functions ------------------------------------------------- + + /// @param dst pointer storing the result + /// @param p pointer to the first point + /// @param q pointer to the second point + /// @param mPtr pointer to free memory + function point_add(dst, p, q, mPtr) { + mstore(mPtr, mload(p)) + mstore(add(mPtr, 0x20), mload(add(p, 0x20))) + mstore(add(mPtr, 0x40), mload(q)) + mstore(add(mPtr, 0x60), mload(add(q, 0x20))) + let l_success := staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + /// @param dst pointer storing the result + /// @param p pointer to the first point (calldata) + /// @param q pointer to the second point (calladata) + /// @param mPtr pointer to free memory + function point_add_calldata(dst, p, q, mPtr) { + mstore(mPtr, mload(p)) + mstore(add(mPtr, 0x20), mload(add(p, 0x20))) + mstore(add(mPtr, 0x40), calldataload(q)) + mstore(add(mPtr, 0x60), calldataload(add(q, 0x20))) + let l_success := staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + /// @parma dst pointer storing the result + /// @param src pointer to a point on Bn254(𝔽_p) + /// @param s scalar + /// @param mPtr free memory + function point_mul(dst, src, s, mPtr) { + mstore(mPtr, mload(src)) + mstore(add(mPtr, 0x20), mload(add(src, 0x20))) + mstore(add(mPtr, 0x40), s) + let l_success := staticcall(gas(), EC_MUL, mPtr, 0x60, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + /// @parma dst pointer storing the result + /// @param src pointer to a point on Bn254(𝔽_p) on calldata + /// @param s scalar + /// @param mPtr free memory + function point_mul_calldata(dst, src, s, mPtr) { + mstore(mPtr, calldataload(src)) + mstore(add(mPtr, 0x20), calldataload(add(src, 0x20))) + mstore(add(mPtr, 0x40), s) + let l_success := staticcall(gas(), EC_MUL, mPtr, 0x60, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + /// @notice dst <- dst + [s]src (Elliptic curve) + /// @param dst pointer accumulator point storing the result + /// @param src pointer to the point to multiply and add + /// @param s scalar + /// @param mPtr free memory + function point_acc_mul(dst, src, s, mPtr) { + mstore(mPtr, mload(src)) + mstore(add(mPtr, 0x20), mload(add(src, 0x20))) + mstore(add(mPtr, 0x40), s) + let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40) + mstore(add(mPtr, 0x40), mload(dst)) + mstore(add(mPtr, 0x60), mload(add(dst, 0x20))) + l_success := and(l_success, staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + /// @notice dst <- dst + [s]src (Elliptic curve) + /// @param dst pointer accumulator point storing the result + /// @param src pointer to the point to multiply and add (on calldata) + /// @param s scalar + /// @mPtr free memory + function point_acc_mul_calldata(dst, src, s, mPtr) { + let state := mload(0x40) + mstore(mPtr, calldataload(src)) + mstore(add(mPtr, 0x20), calldataload(add(src, 0x20))) + mstore(add(mPtr, 0x40), s) + let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40) + mstore(add(mPtr, 0x40), mload(dst)) + mstore(add(mPtr, 0x60), mload(add(dst, 0x20))) + l_success := and(l_success, staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + /// @notice dst <- dst + src*s (Fr) dst,src are addresses, s is a value + /// @param dst pointer storing the result + /// @param src pointer to the scalar to multiply and add (on calldata) + /// @param s scalar + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, R_MOD) + mstore(dst, addmod(mload(dst), tmp, R_MOD)) + } + + /// @param x element to exponentiate + /// @param e exponent + /// @param mPtr free memory + /// @return res x ** e mod r + function pow(x, e, mPtr) -> res { + mstore(mPtr, 0x20) + mstore(add(mPtr, 0x20), 0x20) + mstore(add(mPtr, 0x40), 0x20) + mstore(add(mPtr, 0x60), x) + mstore(add(mPtr, 0x80), e) + mstore(add(mPtr, 0xa0), R_MOD) + let check_staticcall := staticcall(gas(), MOD_EXP, mPtr, 0xc0, mPtr, 0x20) + if eq(check_staticcall, 0) {} + res := mload(mPtr) + } + } + } +} diff --git a/contracts/src/v1.0.8-testnet/SP1Verifier.sol b/contracts/src/v1.0.8-testnet/SP1Verifier.sol new file mode 100644 index 0000000..b74705d --- /dev/null +++ b/contracts/src/v1.0.8-testnet/SP1Verifier.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ISP1Verifier, ISP1VerifierWithHash} from "../ISP1Verifier.sol"; +import {PlonkVerifier} from "./PlonkVerifier.sol"; + +/// @title SP1 Verifier +/// @author Succinct Labs +/// @notice This contracts implements a solidity verifier for SP1. +contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { + /// @notice Thrown when the verifier selector from this proof does not match the one in this + /// verifier. This indicates that this proof was sent to the wrong verifier. + /// @param received The verifier selector from the first 4 bytes of the proof. + /// @param expected The verifier selector from the first 4 bytes of the VERIFIER_HASH(). + error WrongVerifierSelector(bytes4 received, bytes4 expected); + + function VERSION() external pure returns (string memory) { + return "v1.0.8-testnet"; + } + + function VERIFIER_HASH() public pure returns (bytes32) { + return 0x801c66ac11bd6d92da378535721468656e5272a6d1923a65ee3b997611d69cc5; + } + + /// @notice Hashes the public values to a field elements inside Bn254. + /// @param publicValues The public values. + function hashPublicValues(bytes calldata publicValues) public pure returns (bytes32) { + return sha256(publicValues) & bytes32(uint256((1 << 253) - 1)); + } + + /// @notice Verifies a proof with given public values and vkey. + /// @param vkey The verification key for the RISC-V program. + /// @param publicValues The public values encoded as bytes. + /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. + function verifyProof(bytes32 vkey, bytes calldata publicValues, bytes calldata proofBytes) + public + view + { + bytes4 recievedSelector = bytes4(proofBytes[:4]); + bytes4 expectedSelector = bytes4(VERIFIER_HASH()); + if (recievedSelector != expectedSelector) { + revert WrongVerifierSelector(recievedSelector, expectedSelector); + } + + bytes32 publicValuesDigest = hashPublicValues(publicValues); + uint256[] memory inputs = new uint256[](2); + inputs[0] = uint256(vkey); + inputs[1] = uint256(publicValuesDigest); + this.Verify(proofBytes[4:], inputs); + } +} From 8786ffe8378cea01daa6ee603ed1d30af4c79f3e Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 11:57:06 -0700 Subject: [PATCH 03/13] read/write json works, TODO better multichain, handle diff verifiers well --- contracts/.gitignore | 3 +- contracts/deployments/11155111.json | 5 +- contracts/deployments/421614.json | 5 +- contracts/foundry.toml | 5 +- contracts/script/utils/Base.s.sol | 72 +++++++++++++++++------------ contracts/src/SP1MockVerifier.sol | 10 +--- 6 files changed, 55 insertions(+), 45 deletions(-) diff --git a/contracts/.gitignore b/contracts/.gitignore index e668838..268454c 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -9,5 +9,4 @@ broadcast/ docs/ # Dotenv files -.env -.env.deployments \ No newline at end of file +.env \ No newline at end of file diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index a699be3..3cca36a 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,4 +1,5 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000003", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" } \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json index a699be3..3cca36a 100644 --- a/contracts/deployments/421614.json +++ b/contracts/deployments/421614.json @@ -1,4 +1,5 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000003", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" } \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 99de8e8..0ce05c3 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -5,7 +5,10 @@ libs = ["lib"] solc = '0.8.20' optimizer = true optimizer_runs = 200 -fs_permissions = [{ access = "read-write", path = "./"}] +fs_permissions = [ + { access = "read", path = "./out" }, + { access = "read-write", path = "./deployments" }, +] [fmt] line_length = 100 diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol index 4649776..45ec3e9 100644 --- a/contracts/script/utils/Base.s.sol +++ b/contracts/script/utils/Base.s.sol @@ -3,11 +3,13 @@ pragma solidity ^0.8.20; import "forge-std/Vm.sol"; import "forge-std/console.sol"; +import {stdJson} from "forge-std/StdJson.sol"; import {Script} from "forge-std/Script.sol"; -import {Strings} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; /// @notice Script to inherit from to get access to helper functions abstract contract BaseScript is Script { + using stdJson for string; + /// @notice Run the command with the `--broadcast` flag to send the transaction to the chain, /// otherwise just simulate the transaction execution. modifier broadcaster() { @@ -34,49 +36,61 @@ abstract contract BaseScript is Script { } } - function deployments() public view returns (string memory) { - return vm.readFile(string.concat("./deployments/", vm.toString(block.chainid), ".json")); + function directory() internal view returns (string memory) { + return string.concat(vm.projectRoot(), "/deployments/"); } - function readAddress(string memory name) internal view returns (address) { - return vm.parseJsonAddress(deployments(), name); + function file() internal view returns (string memory) { + return string.concat(vm.toString(block.chainid), ".json"); } - function readBytes32(string memory name) internal view returns (bytes32) { - return vm.parseJsonBytes32(deployments(), name); + function path() internal view returns (string memory) { + return string.concat(directory(), file()); } - // function writeAddress(string memory name) internal view returns (address) { - // return vm.writeJson(deployments(), name, vm.envAddress(name)); - // } + function deployments() internal view returns (string memory) { + return vm.readFile(path()); + } - function writeAddress(string memory name, address value) internal { - string memory directory = string.concat(vm.projectRoot(), deployments()); - if (!vm.exists(directory)) { - vm.createDir(directory, true); + function ensureExists() internal { + if (!vm.exists(directory())) { + vm.createDir(directory(), true); } - string memory file = - string.concat(vm.projectRoot(), deployments(), vm.toString(block.chainid), ".json"); - bool exists = vm.exists(file); - if (!exists) { - vm.writeFile(file, "{}"); + if (!vm.exists(path())) { + vm.writeFile(path(), "{}"); } + } + + function readAddress(string memory key) internal view returns (address) { + return deployments().readAddress(string.concat(".", key)); + } + + function readBytes32(string memory key) internal view returns (bytes32) { + return deployments().readBytes32(string.concat(".", key)); + } + + function writeAddress(string memory key, address value) internal { + ensureExists(); - string memory json = vm.readFile(file); - if (vm.keyExists(json, string.concat(".", name))) { - vm.writeJson(Strings.toHexString(value), file, string.concat(".", name)); + if (vm.keyExists(deployments(), string.concat(".", key))) { + vm.writeJson(vm.toString(value), path(), string.concat(".", key)); } else { string memory root = "root"; - vm.serializeJson(root, json); - vm.writeJson(vm.serializeAddress(root, name, value), file); + vm.serializeJson(root, deployments()); + vm.writeJson(vm.serializeAddress(root, key, value), path()); } } - function writeEnvAddress(string memory file, string memory name, address value) internal { - string memory addrVar = string.concat(name, "_", vm.toString(block.chainid)); - vm.setEnv(addrVar, Strings.toHexString(value)); - vm.writeLine(file, string.concat(string.concat(addrVar, "="), Strings.toHexString(value))); - console.log(string.concat(string.concat(addrVar, "="), Strings.toHexString(value))); + function writeBytes32(string memory key, bytes32 value) internal { + ensureExists(); + + if (vm.keyExists(deployments(), string.concat(".", key))) { + vm.writeJson(vm.toString(value), path(), string.concat(".", key)); + } else { + string memory root = "root"; + vm.serializeJson(root, deployments()); + vm.writeJson(vm.serializeBytes32(root, key, value), path()); + } } } diff --git a/contracts/src/SP1MockVerifier.sol b/contracts/src/SP1MockVerifier.sol index 0ca6f4a..b403d02 100644 --- a/contracts/src/SP1MockVerifier.sol +++ b/contracts/src/SP1MockVerifier.sol @@ -7,17 +7,9 @@ import {ISP1Verifier} from "./ISP1Verifier.sol"; /// @author Succinct Labs /// @notice This contracts implements a Mock solidity verifier for SP1. contract SP1MockVerifier is ISP1Verifier { - function VERSION() external pure returns (string memory) { - return "TODO"; - } - /// @notice Verifies a mock proof with given public values and vkey. /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. - function verifyProof( - bytes32, - bytes memory, - bytes memory proofBytes - ) external pure { + function verifyProof(bytes32, bytes calldata, bytes calldata proofBytes) external pure { assert(proofBytes.length == 0); } } From 331789af72ad215294e46ed6ae01eed67ec2678e Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 12:35:59 -0700 Subject: [PATCH 04/13] good --- README.md | 16 ++++- contracts/.env.example | 23 ++++-- contracts/deployments/11155111.json | 7 +- contracts/deployments/421614.json | 5 +- contracts/foundry.toml | 35 ++++++--- contracts/script/deploy/SP1Verifier.s.sol | 8 +-- .../script/deploy/SP1VerifierGateway.sol | 8 +-- contracts/script/utils/Base.s.sol | 19 +++-- contracts/script/verify.sh | 71 ------------------- 9 files changed, 84 insertions(+), 108 deletions(-) delete mode 100755 contracts/script/verify.sh diff --git a/README.md b/README.md index fdcf20b..4071f9f 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ This repository contains the smart contracts for verifying [SP1](https://github. ## Installation -> [!WARNING] -> [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. +> [!WARNING] > [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. To install the latest release version: @@ -14,6 +13,7 @@ forge install succinctlabs/sp1-contracts ``` To install a specific version: + ```bash forge install succinctlabs/sp1-contracts@ ``` @@ -33,6 +33,16 @@ contract MyContract is SP1Verifier { } ``` +### Deployments + +To deploy the contracts, you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway: + +```bash +forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast +``` + +To re-verify already existing deployments, remove the `--broadcast` flag. + ## For Developers: Integrate SP1 Contracts This repository contains the EVM contracts for verifying SP1 PLONK EVM proofs. @@ -41,4 +51,4 @@ You can find more details on the contracts in the [`contracts`](./contracts/READ ## For Contributors -To update the SP1 contracts, please refer to the [`update`](./UPDATE_CONTRACTS.md) file. \ No newline at end of file +To update the SP1 contracts, please refer to the [`update`](./UPDATE_CONTRACTS.md) file. diff --git a/contracts/.env.example b/contracts/.env.example index 759017f..41062f3 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -1,7 +1,7 @@ -### The chains to deploy to +### The chains to deploy to, specified by chain ID (e.g. CHAINS=1,11155111,17000) CHAIN_IDS= -### RPCs for each chain +### RPCs for each chain ID RPC_1= RPC_11155111= RPC_17000= @@ -14,7 +14,7 @@ RPC_84532= RPC_534352= RPC_534351= -# Etherscan API keys for each chain +# Etherscan API keys for each chain ID ETHERSCAN_API_KEY_1= ETHERSCAN_API_KEY_11155111= ETHERSCAN_API_KEY_17000= @@ -25,4 +25,19 @@ ETHERSCAN_API_KEY_421614= ETHERSCAN_API_KEY_8453= ETHERSCAN_API_KEY_84532= ETHERSCAN_API_KEY_534352= -ETHERSCAN_API_KEY_534351= \ No newline at end of file +ETHERSCAN_API_KEY_534351= + +# Etherscan API URLs for each chain ID +ETHERSCAN_API_URL_1=https://api.etherscan.io/api +ETHERSCAN_API_URL_5=https://api-goerli.etherscan.io/api +ETHERSCAN_API_URL_17000=https://api-holesky.etherscan.io/api +ETHERSCAN_API_URL_11155111=https://api-sepolia.etherscan.io/api +ETHERSCAN_API_URL_100=https://api.gnosisscan.io/api +ETHERSCAN_API_URL_137=https://api.polygonscan.com/api +ETHERSCAN_API_URL_420=https://api-optimistic.etherscan.io/api +ETHERSCAN_API_URL_42161=https://api.arbiscan.io/api +ETHERSCAN_API_URL_421614=https://api-sepolia.arbiscan.io/api +ETHERSCAN_API_URL_8453=https://api.basescan.org/api +ETHERSCAN_API_URL_84532=https://api-sepolia.basescan.org/api +ETHERSCAN_API_URL_534352=https://api.scrollscan.com/api +ETHERSCAN_API_URL_534351=https://api-sepolia.scrollscan.com/api \ No newline at end of file diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index 3cca36a..33fea2f 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,5 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" } \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json index 3cca36a..dea8856 100644 --- a/contracts/deployments/421614.json +++ b/contracts/deployments/421614.json @@ -1,5 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" + "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" } \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 0ce05c3..d3d76d3 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -18,15 +18,28 @@ ignore = ["lib/**"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +[rpc_endpoints] +1 = "${RPC_1}" +11155111 = "${RPC_11155111}" +17000 = "${RPC_17000}" +100 = "${RPC_100}" +137 = "${RPC_137}" +42161 = "${RPC_42161}" +421614 = "${RPC_421614}" +8453 = "${RPC_8453}" +84532 = "${RPC_84532}" +534352 = "${RPC_534352}" +534351 = "${RPC_534351}" + [etherscan] -ethereum = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } -sepolia = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } -holesky = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } -gnosis = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } -polygon = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } -arbitrum = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } -arbitrum-sepolia = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } -base = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } -base-sepolia = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } -scroll = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } -scroll-sepolia = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file +1 = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } +11155111 = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } +17000 = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } +100 = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } +137 = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } +42161 = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } +421614 = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } +8453 = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } +84532 = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } +534352 = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } +534351 = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file diff --git a/contracts/script/deploy/SP1Verifier.s.sol b/contracts/script/deploy/SP1Verifier.s.sol index 14ed7f3..d083e13 100644 --- a/contracts/script/deploy/SP1Verifier.s.sol +++ b/contracts/script/deploy/SP1Verifier.s.sol @@ -7,10 +7,10 @@ import {SP1Verifier} from "../../src/v1.0.8-testnet/SP1Verifier.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; contract SP1VerifierScript is BaseScript { - function run() external multichain broadcaster { - console.log("Deploying SP1_VERIFIER on chain %s", vm.toString(block.chainid)); + string internal constant KEY = "SP1_VERIFIER"; - // Read env variables + function run() external multichain(KEY) broadcaster { + // Read config bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); address SP1_VERIFIER_GATEWAY = readAddress("SP1_VERIFIER_GATEWAY"); @@ -22,6 +22,6 @@ contract SP1VerifierScript is BaseScript { gateway.addRoute(verifier); // Write address - writeAddress("SP1_VERIFIER", verifier); + writeAddress(KEY, verifier); } } diff --git a/contracts/script/deploy/SP1VerifierGateway.sol b/contracts/script/deploy/SP1VerifierGateway.sol index dcc2bc3..93cd37e 100644 --- a/contracts/script/deploy/SP1VerifierGateway.sol +++ b/contracts/script/deploy/SP1VerifierGateway.sol @@ -6,10 +6,10 @@ import {BaseScript} from "../utils/Base.s.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; contract SP1VerifierGatewayScript is BaseScript { - function run() external multichain broadcaster { - console.log("Deploying SP1_VERIFIER_GATEWAY on chain %s", vm.toString(block.chainid)); + string internal constant KEY = "SP1_VERIFIER_GATEWAY"; - // Read env variables + function run() external multichain(KEY) broadcaster { + // Read config bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); address OWNER = readAddress("OWNER"); @@ -17,6 +17,6 @@ contract SP1VerifierGatewayScript is BaseScript { address gateway = address(new SP1VerifierGateway{salt: CREATE2_SALT}(OWNER)); // Write address - writeAddress("SP1_VERIFIER_GATEWAY", gateway); + writeAddress(KEY, gateway); } } diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol index 45ec3e9..90b6039 100644 --- a/contracts/script/utils/Base.s.sol +++ b/contracts/script/utils/Base.s.sol @@ -6,7 +6,7 @@ import "forge-std/console.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Script} from "forge-std/Script.sol"; -/// @notice Script to inherit from to get access to helper functions +/// @notice Script to inherit from to get access to helper functions for deployments. abstract contract BaseScript is Script { using stdJson for string; @@ -20,38 +20,41 @@ abstract contract BaseScript is Script { /// @notice When used, runs the script on the chains specified in the `CHAIN_IDS` env variable. /// Must have a `RPC_${CHAIN_ID}` env variable set for each chain. - modifier multichain() { + modifier multichain(string memory KEY) { uint256[] memory chainIds = vm.envUint("CHAIN_IDS", ","); for (uint256 i = 0; i < chainIds.length; i++) { uint256 chainId = chainIds[i]; // Switch to the chain using the RPC - string memory rpc = vm.envString(string.concat("RPC_", vm.toString(chainId))); - vm.createSelectFork(rpc); + vm.createSelectFork(vm.toString(chainId)); - // or try vm.createSelectFork(vm.rpcUrl(chainName)); - // https://github.com/ourzora/zora-protocol/blob/87fd4a0f06cbabe9bb081eb01babc7222bd1db91/packages/frames/script/MultichainScript.sol#L35 + console.log("Deploying %s to chain %s", KEY, vm.toString(block.chainid)); _; } } + /// @notice Returns the directory of the deployments. function directory() internal view returns (string memory) { return string.concat(vm.projectRoot(), "/deployments/"); } + /// @notice Returns the file name for the current chain. function file() internal view returns (string memory) { return string.concat(vm.toString(block.chainid), ".json"); } + /// @notice Returns the path to the deployments file for the current chain. function path() internal view returns (string memory) { return string.concat(directory(), file()); } + /// @notice Returns the deployments file contents for the current chain. function deployments() internal view returns (string memory) { return vm.readFile(path()); } + /// @notice Ensures that the deployments file exists for the current chain. function ensureExists() internal { if (!vm.exists(directory())) { vm.createDir(directory(), true); @@ -62,14 +65,17 @@ abstract contract BaseScript is Script { } } + /// @notice Reads an address from the deployments file for the current chain. function readAddress(string memory key) internal view returns (address) { return deployments().readAddress(string.concat(".", key)); } + /// @notice Reads a bytes32 from the deployments file for the current chain. function readBytes32(string memory key) internal view returns (bytes32) { return deployments().readBytes32(string.concat(".", key)); } + /// @notice Writes an address to the deployments file for the current chain. function writeAddress(string memory key, address value) internal { ensureExists(); @@ -82,6 +88,7 @@ abstract contract BaseScript is Script { } } + /// @notice Writes a bytes32 to the deployments file for the current chain. function writeBytes32(string memory key, bytes32 value) internal { ensureExists(); diff --git a/contracts/script/verify.sh b/contracts/script/verify.sh deleted file mode 100755 index 2cc7a58..0000000 --- a/contracts/script/verify.sh +++ /dev/null @@ -1,71 +0,0 @@ -USAGE="Usage: ./verify.sh \n Example: ./verify.sh \"SP1VerifierGateway\" \"5 420 84531 421613\" \"true\" \"$(cast abi-encode "constructor(address)" "0x6e4f1e9ea315ebfd69d18c2db974eef6105fb803")\"" - -if [ -z "$1" ]; then - echo $USAGE - exit 1 -fi - -if [ -z "$2" ]; then - echo $USAGE - exit 1 -fi - -if [[ -z "$3" || ( "$3" != "true" && "$3" != "false" ) ]]; then - echo "$USAGE" - exit 1 -fi - -CONTRACT=$1 -IFS=' ' read -r -a CHAIN_IDS <<< "$2" -IS_PROXY=$3 -CONSTRUCTOR_ARGS=$4 - -# Load environment variables from .env -source .env - -# Create .env.deployments if it doesn't exist -if [ ! -f .env.deployments ]; then - touch .env.deployments -fi - -for chain_id in "${CHAIN_IDS[@]}"; do - set -a - source .env.deployments - set +a - - contract=$CONTRACT - constructor_args=${CONSTRUCTOR_ARGS[$i]} - is_proxy=${IS_PROXY[$i]} - snake_case=$(echo $contract | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g' | tr '[:lower:]' '[:upper:]') - - address_var=$(echo "${snake_case}_${chain_id}") - address=$(echo $(eval echo "\$$address_var")) - - etherscan_key_var=$(echo 'ETHERSCAN_API_KEY_'"${chain_id}") - etherscan_key=$(echo $(eval echo "\$$etherscan_key_var")) - - # Proxy logic - if $is_proxy; then - rpc_url_var=$(echo 'RPC_'"${chain_id}") - rpc_url=$(echo $(eval echo "\$$rpc_url_var")) - # ERC1967Proxy implementation slot - impl=$(cast storage $address 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $rpc_url) - impl_addr=$(cast abi-decode "a()(address)" $impl) - proxy_constructor_calldata=$(cast abi-encode "constructor(address,bytes)" $impl_addr "$constructor_args") - echo "Verifying Proxy_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" - forge verify-contract $address src/upgrade/Proxy.sol:Proxy --chain ${chain_id} --watch --constructor-args $proxy_constructor_calldata --verifier etherscan --etherscan-api-key $etherscan_key - address=$impl_addr - constructor_args="" - fi - - echo "Verifying ${contract}_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" - if [ -n "$constructor_args" ]; then - forge verify-contract $address $contract --chain ${chain_id} --watch --constructor-args $constructor_args --verifier etherscan --etherscan-api-key $etherscan_key - else - forge verify-contract $address $contract --chain ${chain_id} --watch --verifier etherscan --etherscan-api-key $etherscan_key - fi -done - - -// maybe can be done in forge: https://github.com/ethereum-optimism/optimism/blob/6062bc14aa887cbbb186377103b99ae5f2cf76e6/packages/contracts-bedrock/scripts/Artifacts.s.sol#L232 -sepollia, arb-sepolia, base-sepolia \ No newline at end of file From a710a73954f15f214ceaeea37654b24ef7de8649 Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 13:00:23 -0700 Subject: [PATCH 05/13] update README, todo foundry profile --- README.md | 8 ++++++-- contracts/.gitignore | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4071f9f..018faae 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ This repository contains the smart contracts for verifying [SP1](https://github. ## Installation -> [!WARNING] > [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. +> [!WARNING] +> +> [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. To install the latest release version: @@ -35,7 +37,9 @@ contract MyContract is SP1Verifier { ### Deployments -To deploy the contracts, you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway: +To deploy the contracts, ensure your [.env](./contracts/.env.example) file is configured with all the chains you want to deploy to. + +Then you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway you can run: ```bash forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast diff --git a/contracts/.gitignore b/contracts/.gitignore index 268454c..3056dd4 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -8,5 +8,5 @@ broadcast/ # Docs docs/ -# Dotenv files +# Dotenv file .env \ No newline at end of file From 18c87daa22982dcef7675368271203ff4cd3ec77 Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 13:08:30 -0700 Subject: [PATCH 06/13] foundry profile --- README.md | 4 +++- contracts/foundry.toml | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 018faae..6891a10 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ To deploy the contracts, ensure your [.env](./contracts/.env.example) file is co Then you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway you can run: ```bash -forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast +FOUNDRY_PROFILE=deploy forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast ``` To re-verify already existing deployments, remove the `--broadcast` flag. @@ -53,6 +53,8 @@ This repository contains the EVM contracts for verifying SP1 PLONK EVM proofs. You can find more details on the contracts in the [`contracts`](./contracts/README.md) directory. +Note: you should ensure that all the contracts are on Solidity version `0.8.20`. + ## For Contributors To update the SP1 contracts, please refer to the [`update`](./UPDATE_CONTRACTS.md) file. diff --git a/contracts/foundry.toml b/contracts/foundry.toml index d3d76d3..1b3ec7b 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -3,13 +3,15 @@ src = "src" out = "out" libs = ["lib"] solc = '0.8.20' -optimizer = true -optimizer_runs = 200 fs_permissions = [ { access = "read", path = "./out" }, { access = "read-write", path = "./deployments" }, ] +[profile.deploy] +optimizer = true +optimizer_runs = 200 + [fmt] line_length = 100 tab_width = 4 From be597c3fb7efb17f63230bf46e449af4bd93de64 Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 15:08:39 -0700 Subject: [PATCH 07/13] received --- contracts/deployments/11155111.json | 8 ++++---- contracts/src/v1.0.7-testnet/SP1Verifier.sol | 6 +++--- contracts/src/v1.0.8-testnet/SP1Verifier.sol | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index 33fea2f..dea8856 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,6 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", - "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" } \ No newline at end of file diff --git a/contracts/src/v1.0.7-testnet/SP1Verifier.sol b/contracts/src/v1.0.7-testnet/SP1Verifier.sol index 255504b..65b4003 100644 --- a/contracts/src/v1.0.7-testnet/SP1Verifier.sol +++ b/contracts/src/v1.0.7-testnet/SP1Verifier.sol @@ -35,10 +35,10 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { bytes calldata publicValues, bytes calldata proofBytes ) external view { - bytes4 recievedSelector = bytes4(proofBytes[:4]); + bytes4 receivedSelector = bytes4(proofBytes[:4]); bytes4 expectedSelector = bytes4(VERIFIER_HASH()); - if (recievedSelector != expectedSelector) { - revert WrongVerifierSelector(recievedSelector, expectedSelector); + if (receivedSelector != expectedSelector) { + revert WrongVerifierSelector(receivedSelector, expectedSelector); } bytes32 publicValuesDigest = hashPublicValues(publicValues); diff --git a/contracts/src/v1.0.8-testnet/SP1Verifier.sol b/contracts/src/v1.0.8-testnet/SP1Verifier.sol index b74705d..27d6594 100644 --- a/contracts/src/v1.0.8-testnet/SP1Verifier.sol +++ b/contracts/src/v1.0.8-testnet/SP1Verifier.sol @@ -36,10 +36,10 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { public view { - bytes4 recievedSelector = bytes4(proofBytes[:4]); + bytes4 receivedSelector = bytes4(proofBytes[:4]); bytes4 expectedSelector = bytes4(VERIFIER_HASH()); - if (recievedSelector != expectedSelector) { - revert WrongVerifierSelector(recievedSelector, expectedSelector); + if (receivedSelector != expectedSelector) { + revert WrongVerifierSelector(receivedSelector, expectedSelector); } bytes32 publicValuesDigest = hashPublicValues(publicValues); From 3e3610b4c5e8c681e14f9a128b3b49512b56fb34 Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 20 Jun 2024 15:26:07 -0700 Subject: [PATCH 08/13] nit --- contracts/src/v1.0.8-testnet/SP1Verifier.sol | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/contracts/src/v1.0.8-testnet/SP1Verifier.sol b/contracts/src/v1.0.8-testnet/SP1Verifier.sol index 27d6594..3f387b2 100644 --- a/contracts/src/v1.0.8-testnet/SP1Verifier.sol +++ b/contracts/src/v1.0.8-testnet/SP1Verifier.sol @@ -28,14 +28,15 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { return sha256(publicValues) & bytes32(uint256((1 << 253) - 1)); } - /// @notice Verifies a proof with given public values and vkey. - /// @param vkey The verification key for the RISC-V program. + /// @notice Verifies a proof with given public values and programVKey. + /// @param programVKey The verification key for the RISC-V program. /// @param publicValues The public values encoded as bytes. /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. - function verifyProof(bytes32 vkey, bytes calldata publicValues, bytes calldata proofBytes) - public - view - { + function verifyProof( + bytes32 programVKey, + bytes calldata publicValues, + bytes calldata proofBytes + ) public view { bytes4 receivedSelector = bytes4(proofBytes[:4]); bytes4 expectedSelector = bytes4(VERIFIER_HASH()); if (receivedSelector != expectedSelector) { @@ -44,7 +45,7 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { bytes32 publicValuesDigest = hashPublicValues(publicValues); uint256[] memory inputs = new uint256[](2); - inputs[0] = uint256(vkey); + inputs[0] = uint256(programVKey); inputs[1] = uint256(publicValuesDigest); this.Verify(proofBytes[4:], inputs); } From da780a5fa3c53be431c08a625f4ecea77f02a055 Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 26 Jun 2024 10:56:15 -0700 Subject: [PATCH 09/13] seperate version deployments --- contracts/deployments/11155111.json | 8 +++--- contracts/deployments/421614.json | 4 +-- .../{ => v1.0.7-testnet}/SP1Verifier.s.sol | 8 +++--- .../deploy/v1.0.8-testnet/SP1Verifier.s.sol | 27 +++++++++++++++++++ contracts/src/v1.0.7-testnet/SP1Verifier.sol | 8 +++++- contracts/src/v1.0.8-testnet/SP1Verifier.sol | 8 +++++- 6 files changed, 51 insertions(+), 12 deletions(-) rename contracts/script/deploy/{ => v1.0.7-testnet}/SP1Verifier.s.sol (72%) create mode 100644 contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index dea8856..7796d6d 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,6 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", - "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937", + "V1_0_8_TESTNET_SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083" } \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json index dea8856..ba0c872 100644 --- a/contracts/deployments/421614.json +++ b/contracts/deployments/421614.json @@ -1,6 +1,6 @@ { "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", - "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937", + "V1_0_8_TESTNET_SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083" } \ No newline at end of file diff --git a/contracts/script/deploy/SP1Verifier.s.sol b/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol similarity index 72% rename from contracts/script/deploy/SP1Verifier.s.sol rename to contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol index d083e13..d4f0797 100644 --- a/contracts/script/deploy/SP1Verifier.s.sol +++ b/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.20; import {console} from "forge-std/console.sol"; -import {BaseScript} from "../utils/Base.s.sol"; -import {SP1Verifier} from "../../src/v1.0.8-testnet/SP1Verifier.sol"; -import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; +import {BaseScript} from "../../utils/Base.s.sol"; +import {SP1Verifier} from "../../../src/v1.0.7-testnet/SP1Verifier.sol"; +import {SP1VerifierGateway} from "../../../src/SP1VerifierGateway.sol"; contract SP1VerifierScript is BaseScript { - string internal constant KEY = "SP1_VERIFIER"; + string internal constant KEY = "V1_0_7_TESTNET_SP1_VERIFIER"; function run() external multichain(KEY) broadcaster { // Read config diff --git a/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol b/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol new file mode 100644 index 0000000..687fa89 --- /dev/null +++ b/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {console} from "forge-std/console.sol"; +import {BaseScript} from "../../utils/Base.s.sol"; +import {SP1Verifier} from "../../../src/v1.0.8-testnet/SP1Verifier.sol"; +import {SP1VerifierGateway} from "../../../src/SP1VerifierGateway.sol"; + +contract SP1VerifierScript is BaseScript { + string internal constant KEY = "V1_0_8_TESTNET_SP1_VERIFIER"; + + function run() external multichain(KEY) broadcaster { + // Read config + bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); + address SP1_VERIFIER_GATEWAY = readAddress("SP1_VERIFIER_GATEWAY"); + + // Deploy contract + address verifier = address(new SP1Verifier{salt: CREATE2_SALT}()); + + // Add the verifier to the gateway + SP1VerifierGateway gateway = SP1VerifierGateway(SP1_VERIFIER_GATEWAY); + gateway.addRoute(verifier); + + // Write address + writeAddress(KEY, verifier); + } +} diff --git a/contracts/src/v1.0.7-testnet/SP1Verifier.sol b/contracts/src/v1.0.7-testnet/SP1Verifier.sol index 65b4003..4ed2bc3 100644 --- a/contracts/src/v1.0.7-testnet/SP1Verifier.sol +++ b/contracts/src/v1.0.7-testnet/SP1Verifier.sol @@ -14,6 +14,9 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { /// @param expected The verifier selector from the first 4 bytes of the VERIFIER_HASH(). error WrongVerifierSelector(bytes4 received, bytes4 expected); + /// @notice Thrown when the proof is invalid. + error InvalidProof(); + function VERSION() external pure returns (string memory) { return "v1.0.7-testnet"; } @@ -45,6 +48,9 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { uint256[] memory inputs = new uint256[](2); inputs[0] = uint256(programVKey); inputs[1] = uint256(publicValuesDigest); - this.Verify(proofBytes[4:], inputs); + bool success = this.Verify(proofBytes[4:], inputs); + if (!success) { + revert InvalidProof(); + } } } diff --git a/contracts/src/v1.0.8-testnet/SP1Verifier.sol b/contracts/src/v1.0.8-testnet/SP1Verifier.sol index 3f387b2..04240f6 100644 --- a/contracts/src/v1.0.8-testnet/SP1Verifier.sol +++ b/contracts/src/v1.0.8-testnet/SP1Verifier.sol @@ -14,6 +14,9 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { /// @param expected The verifier selector from the first 4 bytes of the VERIFIER_HASH(). error WrongVerifierSelector(bytes4 received, bytes4 expected); + /// @notice Thrown when the proof is invalid. + error InvalidProof(); + function VERSION() external pure returns (string memory) { return "v1.0.8-testnet"; } @@ -47,6 +50,9 @@ contract SP1Verifier is PlonkVerifier, ISP1VerifierWithHash { uint256[] memory inputs = new uint256[](2); inputs[0] = uint256(programVKey); inputs[1] = uint256(publicValuesDigest); - this.Verify(proofBytes[4:], inputs); + bool success = this.Verify(proofBytes[4:], inputs); + if (!success) { + revert InvalidProof(); + } } } From 1148b712d503e25943dd1e86fde9c96b5d0106e5 Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 26 Jun 2024 11:48:38 -0700 Subject: [PATCH 10/13] update env example --- contracts/.env.example | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/.env.example b/contracts/.env.example index 41062f3..cf7f750 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -1,3 +1,9 @@ +### Salt used to deploy the contracts. Recommended to use the same salt across different chains. +CREATE2_SALT= + +### The owner of the SP1 Verifier Gateway. This is the account that will be able to add and freeze routes. +OWNER= + ### The chains to deploy to, specified by chain ID (e.g. CHAINS=1,11155111,17000) CHAIN_IDS= From c2e2cfbb4fc9ee470f81e8ec6fe909c7b90ecadf Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 26 Jun 2024 12:32:53 -0700 Subject: [PATCH 11/13] read env, go through full flow e2e again --- README.md | 10 +++++++++- contracts/deployments/11155111.json | 7 +++---- contracts/deployments/421614.json | 7 +++---- ...erGateway.sol => SP1VerifierGateway.s.sol} | 1 - .../deploy/v1.0.7-testnet/SP1Verifier.s.sol | 1 - .../deploy/v1.0.8-testnet/SP1Verifier.s.sol | 1 - contracts/script/utils/Base.s.sol | 20 +++++++++++++++++-- 7 files changed, 33 insertions(+), 14 deletions(-) rename contracts/script/deploy/{SP1VerifierGateway.sol => SP1VerifierGateway.s.sol} (93%) diff --git a/README.md b/README.md index 6891a10..c52ccf7 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,17 @@ To deploy the contracts, ensure your [.env](./contracts/.env.example) file is co Then you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway you can run: ```bash -FOUNDRY_PROFILE=deploy forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast +FOUNDRY_PROFILE=deploy forge script ./script/deploy/SP1VerifierGateway.s.sol:SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast ``` +To deploy a specific SP1 Verifier version and add it to the gateway, run: + +```bash +FOUNDRY_PROFILE=deploy forge script ./script/deploy/v1.0.8-testnet/SP1Verifier.s.sol:SP1VerifierScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast +``` + +Change `v1.0.8-testnet` to the desired version to add. + To re-verify already existing deployments, remove the `--broadcast` flag. ## For Developers: Integrate SP1 Contracts diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index 7796d6d..880c176 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,6 +1,5 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937", - "V1_0_8_TESTNET_SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083" + "SP1_VERIFIER_GATEWAY": "0x3B6041173B80E77f038f3F2C0f9744f04837185e", + "V1_0_7_TESTNET_SP1_VERIFIER": "0x331b350dDA287d0A65ce43103984CD44cb4Da9f0", + "V1_0_8_TESTNET_SP1_VERIFIER": "0xfE2bb0Ad7F2c44Bd1289234Af08aD6FDEC0d54a2" } \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json index ba0c872..880c176 100644 --- a/contracts/deployments/421614.json +++ b/contracts/deployments/421614.json @@ -1,6 +1,5 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937", - "V1_0_8_TESTNET_SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083" + "SP1_VERIFIER_GATEWAY": "0x3B6041173B80E77f038f3F2C0f9744f04837185e", + "V1_0_7_TESTNET_SP1_VERIFIER": "0x331b350dDA287d0A65ce43103984CD44cb4Da9f0", + "V1_0_8_TESTNET_SP1_VERIFIER": "0xfE2bb0Ad7F2c44Bd1289234Af08aD6FDEC0d54a2" } \ No newline at end of file diff --git a/contracts/script/deploy/SP1VerifierGateway.sol b/contracts/script/deploy/SP1VerifierGateway.s.sol similarity index 93% rename from contracts/script/deploy/SP1VerifierGateway.sol rename to contracts/script/deploy/SP1VerifierGateway.s.sol index 93cd37e..05178d9 100644 --- a/contracts/script/deploy/SP1VerifierGateway.sol +++ b/contracts/script/deploy/SP1VerifierGateway.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {console} from "forge-std/console.sol"; import {BaseScript} from "../utils/Base.s.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; diff --git a/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol b/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol index d4f0797..e6d1544 100644 --- a/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol +++ b/contracts/script/deploy/v1.0.7-testnet/SP1Verifier.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {console} from "forge-std/console.sol"; import {BaseScript} from "../../utils/Base.s.sol"; import {SP1Verifier} from "../../../src/v1.0.7-testnet/SP1Verifier.sol"; import {SP1VerifierGateway} from "../../../src/SP1VerifierGateway.sol"; diff --git a/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol b/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol index 687fa89..0d65cd4 100644 --- a/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol +++ b/contracts/script/deploy/v1.0.8-testnet/SP1Verifier.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {console} from "forge-std/console.sol"; import {BaseScript} from "../../utils/Base.s.sol"; import {SP1Verifier} from "../../../src/v1.0.8-testnet/SP1Verifier.sol"; import {SP1VerifierGateway} from "../../../src/SP1VerifierGateway.sol"; diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol index 90b6039..35c37bf 100644 --- a/contracts/script/utils/Base.s.sol +++ b/contracts/script/utils/Base.s.sol @@ -65,13 +65,29 @@ abstract contract BaseScript is Script { } } - /// @notice Reads an address from the deployments file for the current chain. + /// @notice Tries to read an address from the env. + function envAddress(string memory key) internal view returns (address) { + return vm.envOr(key, address(0)); + } + + /// @notice Tries to read a bytes32 from the env. + function envBytes32(string memory key) internal view returns (bytes32) { + return vm.envOr(key, bytes32(0)); + } + + /// @notice Tries to read an address from the env first, then from the deployments file for the current chain. function readAddress(string memory key) internal view returns (address) { + if (envAddress(key) != address(0)) { + return envAddress(key); + } return deployments().readAddress(string.concat(".", key)); } - /// @notice Reads a bytes32 from the deployments file for the current chain. + /// @notice Tries to read a bytes32 from the env first, then from the deployments file for the current chain. function readBytes32(string memory key) internal view returns (bytes32) { + if (envBytes32(key) != bytes32(0)) { + return envBytes32(key); + } return deployments().readBytes32(string.concat(".", key)); } From 846de8a70a77e2e3340104e98a9faf8544065dcd Mon Sep 17 00:00:00 2001 From: mattstam Date: Wed, 26 Jun 2024 13:25:22 -0700 Subject: [PATCH 12/13] chain name --- contracts/.env.example | 66 ++++++++++++++----------------- contracts/foundry.toml | 40 +++++++++---------- contracts/script/utils/Base.s.sol | 14 +++---- 3 files changed, 54 insertions(+), 66 deletions(-) diff --git a/contracts/.env.example b/contracts/.env.example index cf7f750..ebf29e4 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -4,46 +4,38 @@ CREATE2_SALT= ### The owner of the SP1 Verifier Gateway. This is the account that will be able to add and freeze routes. OWNER= -### The chains to deploy to, specified by chain ID (e.g. CHAINS=1,11155111,17000) -CHAIN_IDS= +### The chains to deploy to, specified by chain name (e.g. CHAINS=mainnet,sepolia,arbitrum_sepolia) +CHAINS= ### RPCs for each chain ID -RPC_1= -RPC_11155111= -RPC_17000= -RPC_100= -RPC_137= -RPC_42161= -RPC_421614= -RPC_8453= -RPC_84532= -RPC_534352= -RPC_534351= +RPC_MAINNET= +RPC_SEPOLIA= +RPC_HOLESKY= +RPC_ARBITRUM= +RPC_ARBITRUM_SEPOLIA= +RPC_BASE= +RPC_BASE_SEPOLIA= +RPC_SCROLL= +RPC_SCROLL_SEPOLIA= # Etherscan API keys for each chain ID -ETHERSCAN_API_KEY_1= -ETHERSCAN_API_KEY_11155111= -ETHERSCAN_API_KEY_17000= -ETHERSCAN_API_KEY_100= -ETHERSCAN_API_KEY_137= -ETHERSCAN_API_KEY_42161= -ETHERSCAN_API_KEY_421614= -ETHERSCAN_API_KEY_8453= -ETHERSCAN_API_KEY_84532= -ETHERSCAN_API_KEY_534352= -ETHERSCAN_API_KEY_534351= +ETHERSCAN_API_KEY_MAINNET= +ETHERSCAN_API_KEY_SEPOLIA= +ETHERSCAN_API_KEY_HOLESKY= +ETHERSCAN_API_KEY_ARBITRUM= +ETHERSCAN_API_KEY_ARBITRUM_SEPOLIA= +ETHERSCAN_API_KEY_BASE= +ETHERSCAN_API_KEY_BASE_SEPOLIA= +ETHERSCAN_API_KEY_SCROLL= +ETHERSCAN_API_KEY_SCROLL_SEPOLIA= # Etherscan API URLs for each chain ID -ETHERSCAN_API_URL_1=https://api.etherscan.io/api -ETHERSCAN_API_URL_5=https://api-goerli.etherscan.io/api -ETHERSCAN_API_URL_17000=https://api-holesky.etherscan.io/api -ETHERSCAN_API_URL_11155111=https://api-sepolia.etherscan.io/api -ETHERSCAN_API_URL_100=https://api.gnosisscan.io/api -ETHERSCAN_API_URL_137=https://api.polygonscan.com/api -ETHERSCAN_API_URL_420=https://api-optimistic.etherscan.io/api -ETHERSCAN_API_URL_42161=https://api.arbiscan.io/api -ETHERSCAN_API_URL_421614=https://api-sepolia.arbiscan.io/api -ETHERSCAN_API_URL_8453=https://api.basescan.org/api -ETHERSCAN_API_URL_84532=https://api-sepolia.basescan.org/api -ETHERSCAN_API_URL_534352=https://api.scrollscan.com/api -ETHERSCAN_API_URL_534351=https://api-sepolia.scrollscan.com/api \ No newline at end of file +ETHERSCAN_API_URL_MAINNET=https://api.etherscan.io/api +ETHERSCAN_API_URL_HOLESKY=https://api-holesky.etherscan.io/api +ETHERSCAN_API_URL_SEPOLIA=https://api-sepolia.etherscan.io/api +ETHERSCAN_API_URL_ARBITRUM=https://api.arbiscan.io/api +ETHERSCAN_API_URL_ARBITRUM_SEPOLIA=https://api-sepolia.arbiscan.io/api +ETHERSCAN_API_URL_BASE=https://api.basescan.org/api +ETHERSCAN_API_URL_BASE_SEPOLIA=https://api-sepolia.basescan.org/api +ETHERSCAN_API_URL_SCROLL=https://api.scrollscan.com/api +ETHERSCAN_API_URL_SCROLL_SEPOLIA=https://api-sepolia.scrollscan.com/api \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 1b3ec7b..aac2dce 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -21,27 +21,23 @@ ignore = ["lib/**"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options [rpc_endpoints] -1 = "${RPC_1}" -11155111 = "${RPC_11155111}" -17000 = "${RPC_17000}" -100 = "${RPC_100}" -137 = "${RPC_137}" -42161 = "${RPC_42161}" -421614 = "${RPC_421614}" -8453 = "${RPC_8453}" -84532 = "${RPC_84532}" -534352 = "${RPC_534352}" -534351 = "${RPC_534351}" +mainnet = "${RPC_MAINNET}" +sepolia = "${RPC_SEPOLIA}" +holesky = "${RPC_HOLESKY}" +arbitrum = "${RPC_ARBITRUM}" +arbitrum_sepolia = "${RPC_ARBITRUM_SEPOLIA}" +base = "${RPC_BASE}" +base_sepolia = "${RPC_BASE_SEPOLIA}" +scroll = "${RPC_SCROLL}" +scroll_sepolia = "${RPC_SCROLL_SEPOLIA}" [etherscan] -1 = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } -11155111 = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } -17000 = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } -100 = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } -137 = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } -42161 = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } -421614 = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } -8453 = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } -84532 = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } -534352 = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } -534351 = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file +mainnet = { key = "${ETHERSCAN_API_KEY_MAINNET}", url = "${ETHERSCAN_API_URL_MAINNET}" } +sepolia = { key = "${ETHERSCAN_API_KEY_SEPOLIA}", url = "${ETHERSCAN_API_URL_SEPOLIA}" } +holesky = { key = "${ETHERSCAN_API_KEY_HOLESKY}", url = "${ETHERSCAN_API_URL_HOLESKY}" } +arbitrum = { key = "${ETHERSCAN_API_KEY_ARBITRUM}", url = "${ETHERSCAN_API_URL_ARBITRUM}" } +arbitrum_sepolia = { key = "${ETHERSCAN_API_KEY_ARBITRUM_SEPOLIA}", url = "${ETHERSCAN_API_URL_ARBITRUM_SEPOLIA}" } +base = { key = "${ETHERSCAN_API_KEY_BASE}", url = "${ETHERSCAN_API_URL_BASE}" } +base_sepolia = { key = "${ETHERSCAN_API_KEY_BASE_SEPOLIA}", url = "${ETHERSCAN_API_URL_BASE_SEPOLIA}" } +scroll = { key = "${ETHERSCAN_API_KEY_SCROLL}", url = "${ETHERSCAN_API_URL_SCROLL}" } +scroll_sepolia = { key = "${ETHERSCAN_API_KEY_SCROLL_SEPOLIA}", url = "${ETHERSCAN_API_URL_SCROLL_SEPOLIA}" } \ No newline at end of file diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol index 35c37bf..7de1e9d 100644 --- a/contracts/script/utils/Base.s.sol +++ b/contracts/script/utils/Base.s.sol @@ -18,17 +18,17 @@ abstract contract BaseScript is Script { vm.stopBroadcast(); } - /// @notice When used, runs the script on the chains specified in the `CHAIN_IDS` env variable. - /// Must have a `RPC_${CHAIN_ID}` env variable set for each chain. + /// @notice When used, runs the script on the chains specified in the `CHAINS` env variable. + /// Must have a `RPC_${CHAIN}` env variable set for each chain (e.g. RPC_MAINNET). modifier multichain(string memory KEY) { - uint256[] memory chainIds = vm.envUint("CHAIN_IDS", ","); - for (uint256 i = 0; i < chainIds.length; i++) { - uint256 chainId = chainIds[i]; + string[] memory chains = vm.envString("CHAINS", ","); + for (uint256 i = 0; i < chains.length; i++) { + string memory chain = chains[i]; // Switch to the chain using the RPC - vm.createSelectFork(vm.toString(chainId)); + vm.createSelectFork(chain); - console.log("Deploying %s to chain %s", KEY, vm.toString(block.chainid)); + console.log("Deploying %s to %s", KEY, chain); _; } From c29ad4fe4b59bf4f1389438043b5977573e28c34 Mon Sep 17 00:00:00 2001 From: mattstam Date: Thu, 27 Jun 2024 13:54:41 -0700 Subject: [PATCH 13/13] rm README versioning statement --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index c52ccf7..8ac0ceb 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,6 @@ To install the latest release version: forge install succinctlabs/sp1-contracts ``` -To install a specific version: - -```bash -forge install succinctlabs/sp1-contracts@ -``` - Add `@sp1-contracts/=lib/sp1-contracts/contracts/src/` in `remappings.txt.` ### Usage