From 9402efa8ed8bd1c6fd816394dfdb2bbeb3ceebb5 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 11 Oct 2023 19:38:45 -0700 Subject: [PATCH 01/30] modified verifier --- go.mod | 18 ++-- go.sum | 22 +++++ plonky2x/core/src/backend/function/mod.rs | 49 ++++----- plonky2x/core/src/backend/wrapper/wrap.rs | 108 +++++--------------- plonky2x/core/src/frontend/vars/bytes32.rs | 17 +++- plonky2x/verifier/circuit.go | 110 ++++++++------------- plonky2x/verifier/cli.go | 15 --- plonky2x/verifier/prover.go | 13 ++- plonky2x/verifier/verifier_test.go | 35 ++++--- 9 files changed, 168 insertions(+), 219 deletions(-) diff --git a/go.mod b/go.mod index 4b475fa73..52699ef41 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/succinctlabs/sdk go 1.20 require ( - github.com/consensys/gnark v0.8.1-0.20230803132917-bd4a39719a96 - github.com/consensys/gnark-crypto v0.11.2 + github.com/consensys/gnark v0.9.0 + github.com/consensys/gnark-crypto v0.12.1 github.com/ethereum/go-ethereum v1.12.0 github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.4 @@ -12,7 +12,7 @@ require ( require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/bits-and-blooms/bitset v1.8.0 // indirect + github.com/bits-and-blooms/bitset v1.9.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect @@ -23,7 +23,7 @@ require ( github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c // indirect @@ -32,16 +32,16 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rs/zerolog v1.30.0 // indirect + github.com/rs/zerolog v1.31.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20230724231837-7bd0035e65e3 // indirect + github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 3a216bc5e..214632f89 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.9.0 h1:g1YivPG8jOtrN013Fe8OBXubkiTwvm7/vG2vXz03ANU= +github.com/bits-and-blooms/bitset v1.9.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= @@ -20,10 +22,14 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark v0.8.1-0.20230803132917-bd4a39719a96 h1:BtDmnURu+BjTQjq0Yu5QIhMnu7BGXwoXEYggzgvFkNc= github.com/consensys/gnark v0.8.1-0.20230803132917-bd4a39719a96/go.mod h1:Y70OuyMjYNvmTUL/sNKhehLOgByRYSOR8X6WMo7GmGQ= +github.com/consensys/gnark v0.9.0 h1:OoOr0Q771mQINVdP3s1AF2Rs1y8gtXhWVkadz/9KmZc= +github.com/consensys/gnark v0.9.0/go.mod h1:Sy9jJjIaGJFfNeupyNOR9Ei2IbAB6cfCO78DfG27YvM= github.com/consensys/gnark-crypto v0.11.1-0.20230724160225-800ddb59f51b h1:tlxRLAcCOcdC3TcP5uqrKaK+OF7eBy1YB5AYUUhOayM= github.com/consensys/gnark-crypto v0.11.1-0.20230724160225-800ddb59f51b/go.mod h1:6C2ytC8zmP8uH2GKVfPOjf0Vw3KwMAaUxlCPK5WQqmw= github.com/consensys/gnark-crypto v0.11.2 h1:GJjjtWJ+db1xGao7vTsOgAOGgjfPe7eRGPL+xxMX0qE= github.com/consensys/gnark-crypto v0.11.2/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -58,6 +64,8 @@ github.com/google/pprof v0.0.0-20230728192033-2ba5b33183c6 h1:ZgoomqkdjGbQ3+qQXC github.com/google/pprof v0.0.0-20230728192033-2ba5b33183c6/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -100,6 +108,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -112,6 +122,10 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20230724231837-7bd0035e65e3 h1:2UL4wMVXtuGnE7f+Bt7gTtMg3bU8Bh3xjNMiR7wzrT0= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20230724231837-7bd0035e65e3/go.mod h1:5jUXJKCi/CwL0U2YWvwKR39mvLH5yV9f7ql+skd0eC8= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012004323-13624e4daf03 h1:xgFRFTvL4357IAs/PE3OhouIL52jG+2WkV7GziiPdbQ= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012004323-13624e4daf03/go.mod h1:5jUXJKCi/CwL0U2YWvwKR39mvLH5yV9f7ql+skd0eC8= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec h1:xMlCVbabspLQ/sukwyu1WQzznh476x4O2/ee5X8igZ0= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -124,9 +138,13 @@ golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -138,8 +156,12 @@ golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 548436590..7ebd0cb21 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -27,8 +27,13 @@ const VERIFIER_CONTRACT: &str = include_str!("../../../../../assets/Verifier.sol /// `Plonky2xFunction`s have all necessary code for a circuit to be deployed end-to-end. pub trait Plonky2xFunction { /// Builds the circuit and saves it to disk. - fn build, const D: usize>(args: BuildArgs) - where + fn build< + L: PlonkParameters, + WrapperParameters: PlonkParameters, + const D: usize, + >( + args: BuildArgs, + ) where <>::Config as GenericConfig>::Hasher: AlgebraicHasher; /// Generates a proof for the circuit and saves it to disk. @@ -52,8 +57,13 @@ pub trait Plonky2xFunction { } impl Plonky2xFunction for C { - fn build, const D: usize>(args: BuildArgs) - where + fn build< + L: PlonkParameters, + WrapperParameters: PlonkParameters, + const D: usize, + >( + args: BuildArgs, + ) where <>::Config as GenericConfig>::Hasher: AlgebraicHasher, { // Build the circuit. @@ -81,7 +91,9 @@ impl Plonky2xFunction for C { let contract_path = format!("{}/FunctionVerifier.sol", args.build_dir); let mut contract_file = File::create(&contract_path).unwrap(); - let circuit_digest_bytes = circuit + let wrapped_circuit = WrappedCircuit::::build(circuit); + let circuit_digest_bytes = wrapped_circuit + .wrapper_circuit .data .verifier_only .circuit_digest @@ -89,28 +101,16 @@ impl Plonky2xFunction for C { .iter() .flat_map(|e| e.to_canonical_u64().to_be_bytes()) .collect::>(); - let full_circuit_digest_bytes = circuit - .data - .verifier_only - .constants_sigmas_cap - .0 - .iter() - .flat_map(|x| { - x.elements - .iter() - .flat_map(|e| e.to_canonical_u64().to_be_bytes()) - }) - .chain(circuit_digest_bytes.iter().copied()) - .collect::>(); - let circuit_digest_hash = sha2::Sha256::digest(full_circuit_digest_bytes); assert!( - circuit_digest_hash.len() <= 32, - "circuit digest must be <= 32 bytes" + circuit_digest_bytes.len() == 32, + "circuit digest must be == 32 bytes" ); + // TODO: assert that the circuit_digest_bytes are smaller than the bn254 field element + // assert!("") let mut padded = vec![0u8; 32]; - let digest_len = circuit_digest_hash.len(); - padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_hash); + let digest_len = circuit_digest_bytes.len(); + padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_bytes); let circuit_digest = format!("0x{}", hex::encode(padded)); let verifier_contract = Self::verifier(&circuit_digest); @@ -171,6 +171,7 @@ impl Plonky2xFunction for C { ); if let PublicOutput::Bytes(output_bytes) = output { + // TODO: can optimize this by saving the wrapped circuit in build let wrapped_circuit = WrappedCircuit::::build(circuit); let wrapped_proof = wrapped_circuit.prove(&proof).expect("failed to wrap proof"); @@ -228,7 +229,7 @@ impl Plonky2xFunction for C { let args = Args::parse(); match args.command { Commands::Build(args) => { - Self::build::(args); + Self::build::(args); } Commands::Prove(args) => { let request = ProofRequest::::load(&args.input_json); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index d870346c0..2f79ecc9c 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -14,10 +14,9 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use serde::Serialize; use crate::backend::circuit::{CircuitBuild, PlonkParameters}; -use crate::frontend::builder::CircuitBuilder; +use crate::frontend::builder::{CircuitBuilder, CircuitIO}; use crate::frontend::hash::sha::sha256::sha256; use crate::frontend::vars::{ByteVariable, Bytes32Variable, CircuitVariable, EvmVariable}; - #[derive(Debug)] pub struct WrappedCircuit< InnerParameters: PlonkParameters, @@ -33,7 +32,7 @@ pub struct WrappedCircuit< recursive_circuit: CircuitBuild, hash_verifier_target: VerifierCircuitTarget, hash_proof_target: ProofWithPublicInputsTarget, - wrapper_circuit: CircuitBuild, + pub wrapper_circuit: CircuitBuild, proof_target: ProofWithPublicInputsTarget, verifier_target: VerifierCircuitTarget, } @@ -58,89 +57,23 @@ where &circuit.data.common, ); - let circuit_digest_bits = circuit_verifier_target - .constants_sigmas_cap - .0 - .iter() - .chain(once(&circuit_verifier_target.circuit_digest)) - .flat_map(|x| x.elements) - .flat_map(|x| { - let mut bits = hash_builder.api.split_le(x, 64); - bits.reverse(); - bits - }) - .collect::>(); - - let circuit_digest_hash: [Target; 256] = - sha256(&mut hash_builder.api, &circuit_digest_bits) - .into_iter() - .map(|x| x.target) - .collect::>() - .try_into() - .unwrap(); - - let circuit_digest_bytes = Bytes32Variable::from_targets(&circuit_digest_hash); - hash_builder.write(circuit_digest_bytes); - - let num_input_targets = circuit - .io - .input() - .iter() - .map(|x| x.targets().len()) - .sum::(); - let (input_targets, output_targets) = circuit_proof_target - .public_inputs - .split_at(num_input_targets); - - let input_bytes = input_targets - .chunks_exact(ByteVariable::nb_elements()) - .map(ByteVariable::from_targets) - .collect::>(); - let output_bytes = output_targets - .chunks_exact(ByteVariable::nb_elements()) - .map(ByteVariable::from_targets) - .collect::>(); - - let input_bits = input_bytes - .iter() - .flat_map(|b| b.to_le_bits::(&mut hash_builder)) - .map(|b| BoolTarget::new_unsafe(b.targets()[0])) - .collect::>(); - - let output_bits = output_bytes - .iter() - .flat_map(|b| b.to_le_bits(&mut hash_builder)) - .map(|b| BoolTarget::new_unsafe(b.targets()[0])) - .collect::>(); - - let mut input_hash = sha256(&mut hash_builder.api, &input_bits) - .into_iter() - .map(|x| x.target) - .collect::>(); - // Remove the last bit to make the hash 255 bits and replace with zero - input_hash.pop(); - input_hash.push(hash_builder.api.constant_bool(false).target); - - let mut output_hash = sha256(&mut hash_builder.api, &output_bits) - .into_iter() - .map(|x| x.target) - .collect::>(); - // Remove the last bit to make the hash 255 bits and replace with zero - output_hash.pop(); - output_hash.push(hash_builder.api.constant_bool(false).target); - - let input_hash_truncated: [Target; 256] = input_hash.try_into().unwrap(); - let output_hash_truncated: [Target; 256] = output_hash.try_into().unwrap(); - - let input_hash_bytes = Bytes32Variable::from_targets(&input_hash_truncated); - let output_hash_bytes = Bytes32Variable::from_targets(&output_hash_truncated); - - hash_builder.write(input_hash_bytes); - hash_builder.write(output_hash_bytes); + match circuit.io { + CircuitIO::Bytes(ref io) => { + let input_bytes = &io.input; + let output_bytes = &io.output; + let input_hash = hash_builder.sha256(input_bytes); + let output_hash = hash_builder.sha256(output_bytes); + let input_hash_zeroed = hash_builder.zero_top_bits(input_hash, 3); + let output_hash_zeroed = hash_builder.zero_top_bits(output_hash, 3); + hash_builder.evm_write(input_hash_zeroed); + hash_builder.evm_write(output_hash_zeroed); + } + _ => unimplemented!(), // TODO: better panic message here + } let hash_circuit = hash_builder.build(); - // An inner recursion to standartize the degree + // An inner recursion to standardize the degree let mut recursive_builder = CircuitBuilder::::new(); let hash_proof_target = recursive_builder.add_virtual_proof_with_pis(&hash_circuit.data.common); @@ -152,6 +85,8 @@ where &hash_circuit.data.common, ); + assert_eq!(hash_proof_target.public_inputs.len(), 32usize * 2); + recursive_builder .api .register_public_inputs(&hash_proof_target.public_inputs); @@ -319,18 +254,21 @@ mod tests { let dummy_path = format!("{}/dummy/", build_path); let mut builder = CircuitBuilder::::new(); - let _ = builder.constant::(F::ONE); + let _ = builder.evm_read::(); // Set up the dummy circuit and wrapper let dummy_circuit = builder.build(); - let dummy_input = dummy_circuit.input(); + let mut dummy_input = dummy_circuit.input(); + dummy_input.evm_write::(0u8); let (dummy_inner_proof, dummy_output) = dummy_circuit.prove(&dummy_input); dummy_circuit.verify(&dummy_inner_proof, &dummy_input, &dummy_output); + println!("Verified dummy_circuit"); let dummy_wrapper = WrappedCircuit::::build(dummy_circuit); let dummy_wrapped_proof = dummy_wrapper.prove(&dummy_inner_proof).unwrap(); dummy_wrapped_proof.save(dummy_path).unwrap(); + println!("Saved dummy_circuit"); // Set up the circuit and wrapper let msg = b"plonky2"; diff --git a/plonky2x/core/src/frontend/vars/bytes32.rs b/plonky2x/core/src/frontend/vars/bytes32.rs index cbf93c9a9..cd0954566 100644 --- a/plonky2x/core/src/frontend/vars/bytes32.rs +++ b/plonky2x/core/src/frontend/vars/bytes32.rs @@ -4,7 +4,8 @@ use ethers::types::H256; use plonky2::hash::hash_types::RichField; use super::{ - ByteVariable, BytesVariable, CircuitVariable, EvmVariable, SSZVariable, U256Variable, Variable, + BoolVariable, ByteVariable, BytesVariable, CircuitVariable, EvmVariable, SSZVariable, + U256Variable, Variable, }; use crate::backend::circuit::PlonkParameters; use crate::frontend::builder::CircuitBuilder; @@ -110,6 +111,20 @@ impl SSZVariable for Bytes32Variable { } } +impl, const D: usize> CircuitBuilder { + // Returns a Bytes32Variable with the first `num_bits` set to 0. + pub fn zero_top_bits(&mut self, original: Bytes32Variable, num_bits: usize) -> Bytes32Variable { + let variables = original.variables(); + let mut new_variables = vec![]; + for _ in 0..num_bits { + new_variables.push(self.zero::()); + } + new_variables.extend_from_slice(&variables[num_bits..]); + + Bytes32Variable::from_variables_unsafe(&new_variables) + } +} + #[cfg(test)] mod test { use ethers::types::U256; diff --git a/plonky2x/verifier/circuit.go b/plonky2x/verifier/circuit.go index 700e16dab..30df17f86 100644 --- a/plonky2x/verifier/circuit.go +++ b/plonky2x/verifier/circuit.go @@ -11,16 +11,13 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/logger" - "github.com/consensys/gnark/test" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" "github.com/succinctlabs/gnark-plonky2-verifier/verifier" ) type Plonky2xVerifierCircuit struct { - ProofWithPis types.ProofWithPublicInputs - VerifierData types.VerifierOnlyCircuitData - - // A digest of the verifier information of the plonky2x circuit. + // A digest of the plonky2x circuit that is being verified. VerifierDigest frontend.Variable `gnark:"verifierDigest,public"` // The input hash is the hash of all onchain inputs into the function. @@ -29,92 +26,67 @@ type Plonky2xVerifierCircuit struct { // The output hash is the hash of all outputs from the function. OutputHash frontend.Variable `gnark:"outputHash,public"` - verifierChip *verifier.VerifierChip `gnark:"-"` - CircuitPath string `gnark:"-"` + // Private inputs to the circuit + ProofWithPis variables.ProofWithPublicInputs + VerifierData variables.VerifierOnlyCircuitData + + // Circuit configuration that is not part of the circuit itself. + CommonCircuitData types.CommonCircuitData `gnark:"-"` } func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { - // load the common circuit data - commonCircuitData := verifier.DeserializeCommonCircuitData(c.CircuitPath + "/common_circuit_data.json") // initialize the verifier chip - c.verifierChip = verifier.NewVerifierChip(api, commonCircuitData) + verifierChip := verifier.NewVerifierChip(api, c.CommonCircuitData) // verify the plonky2 proof - c.verifierChip.Verify(c.ProofWithPis.Proof, c.ProofWithPis.PublicInputs, c.VerifierData, commonCircuitData) + verifierChip.Verify(c.ProofWithPis.Proof, c.ProofWithPis.PublicInputs, c.VerifierData) + // We assume that the publicInputs have 64 bytes + // publicInputs[0:32] is a big-endian representation of a SHA256 hash that has been truncated to 253 bits. + // We truncate to 253 bits because we want to only have `InputHash` be 1 BN254 field element. publicInputs := c.ProofWithPis.PublicInputs - verifierDigestBytes := make([]frontend.Variable, 32) - for i := range verifierDigestBytes { - pubByte := publicInputs[i].Limb - verifierDigestBytes[i] = pubByte + if len(publicInputs) != 64 { + return fmt.Errorf("expected 64 public inputs, got %d", len(publicInputs)) } - verifierDigest := frontend.Variable(0) - for i := range verifierDigestBytes { - verifierDigest = api.Add(verifierDigest, api.Mul(verifierDigestBytes[i], frontend.Variable(1<<(8*i)))) - } - c.VerifierDigest = verifierDigest - inputDigestBytes := make([]frontend.Variable, 32) - for i := range inputDigestBytes { - pubByte := publicInputs[i+32].Limb - inputDigestBytes[i] = pubByte - } inputDigest := frontend.Variable(0) - for i := range inputDigestBytes { - inputDigest = api.Add(inputDigest, api.Mul(inputDigestBytes[i], frontend.Variable(1<<(8*i)))) + for i := 0; i < 32; i++ { + pubByte := publicInputs[31-i].Limb + inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(1<<(8*i)))) } - c.InputHash = inputDigest + api.AssertIsEqual(c.InputHash, inputDigest) - outputDigestBytes := make([]frontend.Variable, 32) - for i := range outputDigestBytes { - pubByte := publicInputs[i+64].Limb - outputDigestBytes[i] = pubByte - } outputDigest := frontend.Variable(0) - for i := range outputDigestBytes { - outputDigest = api.Add(outputDigest, api.Mul(outputDigestBytes[i], frontend.Variable(1<<(8*i)))) + for i := 0; i < 32; i++ { + pubByte := publicInputs[63-i].Limb + outputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(1<<(8*i)))) } - c.OutputHash = outputDigest + api.AssertIsEqual(c.OutputHash, outputDigest) - return nil -} + // Finally we have to assert that the VerifierData we verified the proof with + // matches the VerifierDigest public input. + api.AssertIsEqual(c.VerifierDigest, c.VerifierData.CircuitDigest) -func VerifierCircuitTest(circuitPath string, dummyCircuitPath string) error { - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json") - proofWithPis := verifier.DeserializeProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json") - circuit := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), - CircuitPath: dummyCircuitPath, - } - - verifierOnlyCircuitData = verifier.DeserializeVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json") - proofWithPis = verifier.DeserializeProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") - witness := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), - CircuitPath: dummyCircuitPath, - } - return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) + return nil } func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey, error) { log := logger.Logger() - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json") - proofWithPis := verifier.DeserializeProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json") + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := variables.DeserializeProofWithPublicInputs( + types.ReadProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json"), + ) + commonCircuitData := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") + circuit := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), - CircuitPath: dummyCircuitPath, + ProofWithPis: proofWithPis, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: new(frontend.Variable), + InputHash: new(frontend.Variable), + OutputHash: new(frontend.Variable), + CommonCircuitData: commonCircuitData, } r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) if err != nil { diff --git a/plonky2x/verifier/cli.go b/plonky2x/verifier/cli.go index e5cdb8abf..9169349ca 100644 --- a/plonky2x/verifier/cli.go +++ b/plonky2x/verifier/cli.go @@ -3,9 +3,7 @@ package main import ( _ "embed" "flag" - "fmt" "os" - "time" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/logger" @@ -16,7 +14,6 @@ func main() { dataPath := flag.String("data", "", "data directory") proofFlag := flag.Bool("prove", false, "create a proof") verifyFlag := flag.Bool("verify", false, "verify a proof") - testFlag := flag.Bool("test", false, "test the circuit") compileFlag := flag.Bool("compile", false, "Compile and save the universal verifier circuit") contractFlag := flag.Bool("contract", true, "Generate solidity contract") flag.Parse() @@ -36,18 +33,6 @@ func main() { log.Debug().Msg("Circuit path: " + *circuitPath) log.Debug().Msg("Data path: " + *dataPath) - if *testFlag { - log.Debug().Msg("testing circuit") - start := time.Now() - err := VerifierCircuitTest(*circuitPath, "./data/dummy") - if err != nil { - fmt.Println("verifier test failed:", err) - os.Exit(1) - } - elasped := time.Since(start) - log.Debug().Msg("verifier test succeeded, time: " + elasped.String()) - } - if *compileFlag { log.Info().Msg("compiling verifier circuit") r1cs, pk, vk, err := CompileVerifierCircuit("./data/dummy") diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index 362b8f701..934b751ba 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -16,7 +16,9 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/logger" "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" + gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" + "github.com/succinctlabs/sdk/gnarkx/types" ) @@ -58,8 +60,12 @@ func LoadProverData(path string) (constraint.ConstraintSystem, groth16.ProvingKe func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk groth16.ProvingKey) (groth16.Proof, witness.Witness, error) { log := logger.Logger() - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json") - proofWithPis := verifier.DeserializeProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + gnark_verifier_types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := variables.DeserializeProofWithPublicInputs( + gnark_verifier_types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json"), + ) // Circuit assignment assignment := &Plonky2xVerifierCircuit{ @@ -68,7 +74,6 @@ func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk groth16.Prov VerifierDigest: frontend.Variable(0), InputHash: frontend.Variable(0), OutputHash: frontend.Variable(0), - CircuitPath: circuitPath, } log.Debug().Msg("Generating witness") diff --git a/plonky2x/verifier/verifier_test.go b/plonky2x/verifier/verifier_test.go index 19f119332..8bafc00a6 100644 --- a/plonky2x/verifier/verifier_test.go +++ b/plonky2x/verifier/verifier_test.go @@ -6,7 +6,8 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/test" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" + "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) func TestPlonky2xVerifierCircuit(t *testing.T) { @@ -16,26 +17,36 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { dummyCircuitPath := "./data/dummy" circuitPath := "./data/test_circuit" - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json") - proofWithPis := verifier.DeserializeProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json") + verifierOnlyCircuitDataDummy := variables.DeserializeVerifierOnlyCircuitData( + types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPisDummy := variables.DeserializeProofWithPublicInputs( + types.ReadProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json"), + ) + commonCircuitDataDummy := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") + circuit := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), - CircuitPath: dummyCircuitPath, + ProofWithPis: proofWithPisDummy, + VerifierData: verifierOnlyCircuitDataDummy, + VerifierDigest: new(frontend.Variable), + InputHash: new(frontend.Variable), + OutputHash: new(frontend.Variable), + CommonCircuitData: commonCircuitDataDummy, } - verifierOnlyCircuitData = verifier.DeserializeVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json") - proofWithPis = verifier.DeserializeProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := variables.DeserializeProofWithPublicInputs( + types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json"), + ) + witness := Plonky2xVerifierCircuit{ ProofWithPis: proofWithPis, VerifierData: verifierOnlyCircuitData, VerifierDigest: new(frontend.Variable), InputHash: new(frontend.Variable), OutputHash: new(frontend.Variable), - CircuitPath: dummyCircuitPath, } return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) } From c74218b9dee4d047f503183430d015f2260aa3a1 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 11 Oct 2023 21:54:30 -0700 Subject: [PATCH 02/30] Test now works --- plonky2x/core/src/backend/function/mod.rs | 14 ++--- plonky2x/core/src/backend/wrapper/wrap.rs | 63 ++++++++++++++++------ plonky2x/verifier/circuit.go | 10 +++- plonky2x/verifier/prover.go | 36 ++++++++++--- plonky2x/verifier/verifier_test.go | 66 +++++++++++++++++++---- 5 files changed, 150 insertions(+), 39 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 7ebd0cb21..ce781a248 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -91,6 +91,9 @@ impl Plonky2xFunction for C { let contract_path = format!("{}/FunctionVerifier.sol", args.build_dir); let mut contract_file = File::create(&contract_path).unwrap(); + info!("First building wrapper circuit to get the wrapper circuit digest..."); + // The wrapper circuit digest will get saved in the Solidity smart contract, + // which will use this value as a public input `VerifierDigest` in the Gnark plonky2 verifier let wrapped_circuit = WrappedCircuit::::build(circuit); let circuit_digest_bytes = wrapped_circuit .wrapper_circuit @@ -101,13 +104,12 @@ impl Plonky2xFunction for C { .iter() .flat_map(|e| e.to_canonical_u64().to_be_bytes()) .collect::>(); - assert!( - circuit_digest_bytes.len() == 32, - "circuit digest must be == 32 bytes" - ); - // TODO: assert that the circuit_digest_bytes are smaller than the bn254 field element - // assert!("") + // The VerifierDigest is stored on-chain as a bytes32, so we need to pad it with 0s + // to store it in the Solidity smart contract. + // Note that we don't need to do any sort of truncation of the top bits because the + // circuit digest already lives in the bn254 field because the WrappedCircuit config + // is the Poseidon Bn254 hasher. let mut padded = vec![0u8; 32]; let digest_len = circuit_digest_bytes.len(); padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_bytes); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index 2f79ecc9c..68fe65aa3 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -16,7 +16,9 @@ use serde::Serialize; use crate::backend::circuit::{CircuitBuild, PlonkParameters}; use crate::frontend::builder::{CircuitBuilder, CircuitIO}; use crate::frontend::hash::sha::sha256::sha256; -use crate::frontend::vars::{ByteVariable, Bytes32Variable, CircuitVariable, EvmVariable}; +use crate::frontend::vars::{ + ByteVariable, Bytes32Variable, CircuitVariable, EvmVariable, Variable, +}; #[derive(Debug)] pub struct WrappedCircuit< InnerParameters: PlonkParameters, @@ -57,20 +59,51 @@ where &circuit.data.common, ); - match circuit.io { - CircuitIO::Bytes(ref io) => { - let input_bytes = &io.input; - let output_bytes = &io.output; - let input_hash = hash_builder.sha256(input_bytes); - let output_hash = hash_builder.sha256(output_bytes); - let input_hash_zeroed = hash_builder.zero_top_bits(input_hash, 3); - let output_hash_zeroed = hash_builder.zero_top_bits(output_hash, 3); - hash_builder.evm_write(input_hash_zeroed); - hash_builder.evm_write(output_hash_zeroed); - } - _ => unimplemented!(), // TODO: better panic message here - } + let num_input_targets = circuit.io.input().len(); + let (input_targets, output_targets) = circuit_proof_target + .public_inputs + .split_at(num_input_targets); + + let input_bytes = input_targets + .chunks_exact(ByteVariable::nb_elements()) + .map(ByteVariable::from_targets) + .collect::>(); + let output_bytes = output_targets + .chunks_exact(ByteVariable::nb_elements()) + .map(ByteVariable::from_targets) + .collect::>(); + let input_hash = hash_builder.sha256(&input_bytes); + let output_hash = hash_builder.sha256(&output_bytes); + + // We must truncate the top 3 bits because in the gnark-plonky2-verifier, the input_hash + // and output_hash are both represented as 1 field element in the BN254 field + // to reduce on-chain verification costs. + let input_hash_zeroed = hash_builder.zero_top_bits(input_hash, 3); + let output_hash_zeroed = hash_builder.zero_top_bits(output_hash, 3); + + let input_vars = input_hash_zeroed + .as_bytes() + .iter() + .map(|b| b.to_variable(&mut hash_builder)) + .collect::>(); + + let output_vars = output_hash_zeroed + .as_bytes() + .iter() + .map(|b| b.to_variable(&mut hash_builder)) + .collect::>(); + + // Write input_hash, output_hash to public_inputs + // In the gnark-plonky2-verifier, these 32 bytes get summed to 1 field element + // that is either the input_hash or output_hash (and is exposed as a public input) + input_vars + .clone() + .into_iter() + .chain(output_vars.into_iter()) + .for_each(|v| { + hash_builder.write(v); + }); let hash_circuit = hash_builder.build(); // An inner recursion to standardize the degree @@ -249,7 +282,7 @@ mod tests { utils::setup_logger(); - let build_path = "../plonky2x-verifier/data".to_string(); + let build_path = "../verifier/data".to_string(); let path = format!("{}/test_circuit/", build_path); let dummy_path = format!("{}/dummy/", build_path); diff --git a/plonky2x/verifier/circuit.go b/plonky2x/verifier/circuit.go index 30df17f86..3b08d4ec1 100644 --- a/plonky2x/verifier/circuit.go +++ b/plonky2x/verifier/circuit.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "math/big" "os" "time" @@ -52,14 +53,19 @@ func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { inputDigest := frontend.Variable(0) for i := 0; i < 32; i++ { pubByte := publicInputs[31-i].Limb - inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(1<<(8*i)))) + fmt.Printf("pubByte: %v\n", pubByte) + inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) + fmt.Printf("inputDigest: %v\n", inputDigest) + } + fmt.Printf("inputDigest: %v\n", inputDigest) + fmt.Printf("inputHash: %v\n", c.InputHash) api.AssertIsEqual(c.InputHash, inputDigest) outputDigest := frontend.Variable(0) for i := 0; i < 32; i++ { pubByte := publicInputs[63-i].Limb - outputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(1<<(8*i)))) + outputDigest = api.Add(outputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) } api.AssertIsEqual(c.OutputHash, outputDigest) diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index 934b751ba..eef4d2fab 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -57,23 +57,45 @@ func LoadProverData(path string) (constraint.ConstraintSystem, groth16.ProvingKe return r1cs, pk, nil } +func GetInputHashOutputHash(proofWithPis gnark_verifier_types.ProofWithPublicInputsRaw) (*big.Int, *big.Int) { + publicInputs := proofWithPis.PublicInputs + if len(publicInputs) != 64 { + panic("publicInputs must be 64 bytes") + } + publicInputsBytes := make([]byte, 64) + for i, v := range publicInputs { + publicInputsBytes[i] = byte(v & 0xFF) + } + fmt.Printf("publicInputsBytes: %x\n", publicInputsBytes[0:32]) + inputHash := new(big.Int).SetBytes(publicInputsBytes[0:32]) + outputHash := new(big.Int).SetBytes(publicInputsBytes[32:64]) + if inputHash.BitLen() > 253 { + panic("inputHash must be at most 253 bits") + } + if outputHash.BitLen() > 253 { + panic("outputHash must be at most 253 bits") + } + return inputHash, outputHash +} + func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk groth16.ProvingKey) (groth16.Proof, witness.Witness, error) { log := logger.Logger() verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( gnark_verifier_types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), ) - proofWithPis := variables.DeserializeProofWithPublicInputs( - gnark_verifier_types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json"), - ) + proofWithPis := gnark_verifier_types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") + proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) + + inputHash, outputHash := GetInputHashOutputHash(proofWithPis) // Circuit assignment assignment := &Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, + ProofWithPis: proofWithPisVariable, VerifierData: verifierOnlyCircuitData, - VerifierDigest: frontend.Variable(0), - InputHash: frontend.Variable(0), - OutputHash: frontend.Variable(0), + VerifierDigest: verifierOnlyCircuitData.CircuitDigest, + InputHash: frontend.Variable(inputHash), + OutputHash: frontend.Variable(outputHash), } log.Debug().Msg("Generating witness") diff --git a/plonky2x/verifier/verifier_test.go b/plonky2x/verifier/verifier_test.go index 8bafc00a6..056200299 100644 --- a/plonky2x/verifier/verifier_test.go +++ b/plonky2x/verifier/verifier_test.go @@ -10,6 +10,8 @@ import ( "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) +// To run this test, you must first populate the data directory by running the following test +// in plonky2x: cargo test test_wrapper -- --nocapture func TestPlonky2xVerifierCircuit(t *testing.T) { assert := test.NewAssert(t) @@ -28,25 +30,71 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { circuit := Plonky2xVerifierCircuit{ ProofWithPis: proofWithPisDummy, VerifierData: verifierOnlyCircuitDataDummy, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), + VerifierDigest: frontend.Variable(0), // Can be empty for defining the circuit + InputHash: frontend.Variable(0), + OutputHash: frontend.Variable(0), CommonCircuitData: commonCircuitDataDummy, } verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), ) - proofWithPis := variables.DeserializeProofWithPublicInputs( - types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json"), + proofWithPis := types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") + inputHash, outputHash := GetInputHashOutputHash(proofWithPis) + + proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) + + witness := Plonky2xVerifierCircuit{ + ProofWithPis: proofWithPisVariable, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: verifierOnlyCircuitData.CircuitDigest, + InputHash: frontend.Variable(inputHash), + OutputHash: frontend.Variable(outputHash), + } + return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) + } + + assert.NoError(testCase()) +} + +func TestPlonky2xVerifierCircuitFails(t *testing.T) { + assert := test.NewAssert(t) + + testCase := func() error { + dummyCircuitPath := "./data/dummy" + circuitPath := "./data/test_circuit" + + verifierOnlyCircuitDataDummy := variables.DeserializeVerifierOnlyCircuitData( + types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPisDummy := variables.DeserializeProofWithPublicInputs( + types.ReadProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json"), ) + commonCircuitDataDummy := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") + + circuit := Plonky2xVerifierCircuit{ + ProofWithPis: proofWithPisDummy, + VerifierData: verifierOnlyCircuitDataDummy, + VerifierDigest: frontend.Variable(0), // Can be empty for defining the circuit + InputHash: frontend.Variable(0), + OutputHash: frontend.Variable(0), + CommonCircuitData: commonCircuitDataDummy, + } + + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") + inputHash, outputHash := GetInputHashOutputHash(proofWithPis) + + proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) witness := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, + ProofWithPis: proofWithPisVariable, VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), + VerifierDigest: verifierOnlyCircuitData.CircuitDigest, + InputHash: frontend.Variable(inputHash), + OutputHash: frontend.Variable(outputHash), } return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) } From 2243a392c326649a037793cc692d069d79608c9c Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 11 Oct 2023 22:31:51 -0700 Subject: [PATCH 03/30] cleanup --- plonky2x/core/src/backend/function/mod.rs | 4 ++-- plonky2x/core/src/backend/wrapper/wrap.rs | 17 +++++------------ plonky2x/core/src/frontend/vars/bytes32.rs | 3 +-- plonky2x/verifier/circuit.go | 10 ++++------ plonky2x/verifier/prover.go | 1 - 5 files changed, 12 insertions(+), 23 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index ce781a248..680fcc0fe 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -13,7 +13,6 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut}; pub use request::*; pub use result::*; use serde::Serialize; -use sha2::Digest; use self::args::{BuildArgs, ProveArgs}; use crate::backend::circuit::*; @@ -173,7 +172,8 @@ impl Plonky2xFunction for C { ); if let PublicOutput::Bytes(output_bytes) = output { - // TODO: can optimize this by saving the wrapped circuit in build + // TODO: can optimize this by saving the wrapped circuit in build and loading it from a binary + // instead of rebuilding every time. let wrapped_circuit = WrappedCircuit::::build(circuit); let wrapped_proof = wrapped_circuit.prove(&proof).expect("failed to wrap proof"); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index 68fe65aa3..6b2dd9c1a 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -1,10 +1,8 @@ -use core::iter::once; use std::fs::{self, File}; use std::path::Path; use anyhow::Result; use log::{debug, info}; -use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_data::{ CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData, @@ -14,11 +12,8 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use serde::Serialize; use crate::backend::circuit::{CircuitBuild, PlonkParameters}; -use crate::frontend::builder::{CircuitBuilder, CircuitIO}; -use crate::frontend::hash::sha::sha256::sha256; -use crate::frontend::vars::{ - ByteVariable, Bytes32Variable, CircuitVariable, EvmVariable, Variable, -}; +use crate::frontend::builder::CircuitBuilder; +use crate::frontend::vars::{ByteVariable, CircuitVariable, Variable}; #[derive(Debug)] pub struct WrappedCircuit< InnerParameters: PlonkParameters, @@ -95,12 +90,12 @@ where .collect::>(); // Write input_hash, output_hash to public_inputs - // In the gnark-plonky2-verifier, these 32 bytes get summed to 1 field element - // that is either the input_hash or output_hash (and is exposed as a public input) + // In the gnark-plonky2-verifier, these 64 bytes get summed to 2 field elements that + // correspond to the input_hash and output_hash respectively as public inputs. input_vars .clone() .into_iter() - .chain(output_vars.into_iter()) + .chain(output_vars) .for_each(|v| { hash_builder.write(v); }); @@ -248,7 +243,6 @@ impl, const D: usize> WrappedOutput { #[cfg(test)] mod tests { use hex::decode; - use plonky2::field::types::Field; use super::*; use crate::backend::circuit::{DefaultParameters, Groth16WrapperParameters}; @@ -275,7 +269,6 @@ mod tests { #[test] #[cfg_attr(feature = "ci", ignore)] fn test_wrapper() { - type F = GoldilocksField; const D: usize = 2; type InnerParameters = DefaultParameters; type OuterParameters = Groth16WrapperParameters; diff --git a/plonky2x/core/src/frontend/vars/bytes32.rs b/plonky2x/core/src/frontend/vars/bytes32.rs index cd0954566..d43e00be2 100644 --- a/plonky2x/core/src/frontend/vars/bytes32.rs +++ b/plonky2x/core/src/frontend/vars/bytes32.rs @@ -4,8 +4,7 @@ use ethers::types::H256; use plonky2::hash::hash_types::RichField; use super::{ - BoolVariable, ByteVariable, BytesVariable, CircuitVariable, EvmVariable, SSZVariable, - U256Variable, Variable, + ByteVariable, BytesVariable, CircuitVariable, EvmVariable, SSZVariable, U256Variable, Variable, }; use crate::backend::circuit::PlonkParameters; use crate::frontend::builder::CircuitBuilder; diff --git a/plonky2x/verifier/circuit.go b/plonky2x/verifier/circuit.go index 3b08d4ec1..e1e0a89ce 100644 --- a/plonky2x/verifier/circuit.go +++ b/plonky2x/verifier/circuit.go @@ -43,7 +43,9 @@ func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { // We assume that the publicInputs have 64 bytes // publicInputs[0:32] is a big-endian representation of a SHA256 hash that has been truncated to 253 bits. - // We truncate to 253 bits because we want to only have `InputHash` be 1 BN254 field element. + // Note that this truncation happens in the `WrappedCircuit` when computing the `input_hash` + // The reason for truncation is that we only want 1 public input on-chain for the input hash + // to save on gas costs publicInputs := c.ProofWithPis.PublicInputs if len(publicInputs) != 64 { @@ -53,13 +55,9 @@ func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { inputDigest := frontend.Variable(0) for i := 0; i < 32; i++ { pubByte := publicInputs[31-i].Limb - fmt.Printf("pubByte: %v\n", pubByte) inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) - fmt.Printf("inputDigest: %v\n", inputDigest) } - fmt.Printf("inputDigest: %v\n", inputDigest) - fmt.Printf("inputHash: %v\n", c.InputHash) api.AssertIsEqual(c.InputHash, inputDigest) outputDigest := frontend.Variable(0) @@ -69,7 +67,7 @@ func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { } api.AssertIsEqual(c.OutputHash, outputDigest) - // Finally we have to assert that the VerifierData we verified the proof with + // We have to assert that the VerifierData we verified the proof with // matches the VerifierDigest public input. api.AssertIsEqual(c.VerifierDigest, c.VerifierData.CircuitDigest) diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index eef4d2fab..72779486b 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -66,7 +66,6 @@ func GetInputHashOutputHash(proofWithPis gnark_verifier_types.ProofWithPublicInp for i, v := range publicInputs { publicInputsBytes[i] = byte(v & 0xFF) } - fmt.Printf("publicInputsBytes: %x\n", publicInputsBytes[0:32]) inputHash := new(big.Int).SetBytes(publicInputsBytes[0:32]) outputHash := new(big.Int).SetBytes(publicInputsBytes[32:64]) if inputHash.BitLen() > 253 { From d3e5ab6d256b8ba827ae98baaecdfc0e2bd3e9cd Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 11 Oct 2023 22:39:32 -0700 Subject: [PATCH 04/30] Clean up verifier test --- plonky2x/verifier/verifier_test.go | 68 ++++++++++-------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/plonky2x/verifier/verifier_test.go b/plonky2x/verifier/verifier_test.go index 056200299..9cddc4080 100644 --- a/plonky2x/verifier/verifier_test.go +++ b/plonky2x/verifier/verifier_test.go @@ -15,7 +15,7 @@ import ( func TestPlonky2xVerifierCircuit(t *testing.T) { assert := test.NewAssert(t) - testCase := func() error { + testCase := func(option int64) error { dummyCircuitPath := "./data/dummy" circuitPath := "./data/test_circuit" @@ -51,53 +51,31 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { InputHash: frontend.Variable(inputHash), OutputHash: frontend.Variable(outputHash), } - return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) - } - - assert.NoError(testCase()) -} - -func TestPlonky2xVerifierCircuitFails(t *testing.T) { - assert := test.NewAssert(t) - testCase := func() error { - dummyCircuitPath := "./data/dummy" - circuitPath := "./data/test_circuit" - - verifierOnlyCircuitDataDummy := variables.DeserializeVerifierOnlyCircuitData( - types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), - ) - proofWithPisDummy := variables.DeserializeProofWithPublicInputs( - types.ReadProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json"), - ) - commonCircuitDataDummy := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") - - circuit := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPisDummy, - VerifierData: verifierOnlyCircuitDataDummy, - VerifierDigest: frontend.Variable(0), // Can be empty for defining the circuit - InputHash: frontend.Variable(0), - OutputHash: frontend.Variable(0), - CommonCircuitData: commonCircuitDataDummy, - } - - verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( - types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), - ) - proofWithPis := types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") - inputHash, outputHash := GetInputHashOutputHash(proofWithPis) - - proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) - - witness := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPisVariable, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: verifierOnlyCircuitData.CircuitDigest, - InputHash: frontend.Variable(inputHash), - OutputHash: frontend.Variable(outputHash), + // When an option is turned on, we fuzz the different witness values to check + // that the circuit proof generation will be invalid when the inputs are incorrect. + if option == 1 { + witness.InputHash = frontend.Variable(0) + } else if option == 2 { + witness.OutputHash = frontend.Variable(0) + } else if option == 3 { + witness.VerifierDigest = verifierOnlyCircuitDataDummy.CircuitDigest + } else if option == 4 { + witness.VerifierDigest = frontend.Variable(0) + } else if option == 5 { + witness.ProofWithPis = proofWithPisDummy + } else if option == 6 { + witness.VerifierData = verifierOnlyCircuitDataDummy + } else if option == 7 { + witness.ProofWithPis = proofWithPisDummy + witness.VerifierData = verifierOnlyCircuitDataDummy + witness.VerifierDigest = verifierOnlyCircuitDataDummy.CircuitDigest } return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) } - assert.NoError(testCase()) + assert.NoError(testCase(0)) + for i := 1; i <= 7; i++ { + assert.Error(testCase(int64(i))) + } } From 7d063810391d8b9460adacf1e2f5b893963be753 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 00:12:13 -0700 Subject: [PATCH 05/30] Circuit digest works --- plonky2x/core/examples/evm.rs | 2 ++ plonky2x/core/src/backend/function/mod.rs | 12 ++++++------ plonky2x/verifier/verifier_test.go | 8 +++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plonky2x/core/examples/evm.rs b/plonky2x/core/examples/evm.rs index f98ea70d5..d490a0967 100644 --- a/plonky2x/core/examples/evm.rs +++ b/plonky2x/core/examples/evm.rs @@ -13,6 +13,8 @@ //! //! `./target/release/circuit_function_evm prove --input-json src/bin/circuit_function_evm_input.json` //! +//! cargo run --example evm prove ./examples/evm.json +//! //! Note that this circuit will not work with field-based io. use plonky2x::backend::circuit::{Circuit, PlonkParameters}; diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 680fcc0fe..74650872c 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -8,7 +8,6 @@ use std::{fs, path}; use clap::Parser; use log::info; -use plonky2::field::types::PrimeField64; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut}; pub use request::*; pub use result::*; @@ -94,15 +93,16 @@ impl Plonky2xFunction for C { // The wrapper circuit digest will get saved in the Solidity smart contract, // which will use this value as a public input `VerifierDigest` in the Gnark plonky2 verifier let wrapped_circuit = WrappedCircuit::::build(circuit); - let circuit_digest_bytes = wrapped_circuit + + let mut circuit_digest_bytes = wrapped_circuit .wrapper_circuit .data .verifier_only .circuit_digest - .to_vec() - .iter() - .flat_map(|e| e.to_canonical_u64().to_be_bytes()) - .collect::>(); + .to_bytes(); + // to_bytes() returns the representation as LE, but we want to save it on-chain + // as BE because that is the format of the public input to the Gnark plonky2 verifier. + circuit_digest_bytes.reverse(); // The VerifierDigest is stored on-chain as a bytes32, so we need to pad it with 0s // to store it in the Solidity smart contract. diff --git a/plonky2x/verifier/verifier_test.go b/plonky2x/verifier/verifier_test.go index 9cddc4080..8d36b77eb 100644 --- a/plonky2x/verifier/verifier_test.go +++ b/plonky2x/verifier/verifier_test.go @@ -70,12 +70,18 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { witness.ProofWithPis = proofWithPisDummy witness.VerifierData = verifierOnlyCircuitDataDummy witness.VerifierDigest = verifierOnlyCircuitDataDummy.CircuitDigest + } else if option == 8 { + // Fuzz random parts of the proof + proofWithPis.Proof.OpeningProof.FinalPoly.Coeffs[0][0] = 0 + witness.ProofWithPis = variables.DeserializeProofWithPublicInputs( + proofWithPis, + ) } return test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) } assert.NoError(testCase(0)) - for i := 1; i <= 7; i++ { + for i := 1; i <= 8; i++ { assert.Error(testCase(int64(i))) } } From a4a696af35d3894198fce2062f0478a2c5e3b1e6 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 00:15:52 -0700 Subject: [PATCH 06/30] clippy --- plonky2x/core/src/backend/wrapper/wrap.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index 6b2dd9c1a..baa62f53a 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -248,7 +248,6 @@ mod tests { use crate::backend::circuit::{DefaultParameters, Groth16WrapperParameters}; use crate::frontend::builder::CircuitBuilder; use crate::frontend::hash::sha::sha256::sha256; - use crate::prelude::*; use crate::utils; fn to_bits(msg: Vec) -> Vec { From 5a4471b3ce614bc2b94e9de71528c9e8a0ddf688 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 00:36:48 -0700 Subject: [PATCH 07/30] Changed smart contract --- plonky2x/core/src/backend/function/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 74650872c..545aac899 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -109,6 +109,9 @@ impl Plonky2xFunction for C { // Note that we don't need to do any sort of truncation of the top bits because the // circuit digest already lives in the bn254 field because the WrappedCircuit config // is the Poseidon Bn254 hasher. + // In fact in the Solidity smart contract we should *not* truncate the top 3 bits + // like we do with input_hash and output_hash, as the circuit digest has a + // small probability of being greater than 2^253, as the field modulus is 254 bits. let mut padded = vec![0u8; 32]; let digest_len = circuit_digest_bytes.len(); padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_bytes); @@ -262,7 +265,6 @@ contract FunctionVerifier is IFunctionVerifier, Verifier { abi.decode(_proof, (uint256[2], uint256[2][2], uint256[2])); uint256[3] memory input = [uint256(CIRCUIT_DIGEST), uint256(_inputHash), uint256(_outputHash)]; - input[0] = input[0] & ((1 << 253) - 1); input[1] = input[1] & ((1 << 253) - 1); input[2] = input[2] & ((1 << 253) - 1); From 6943848d8760efb6ae2baed6f0841f00b01daf63 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 01:31:10 -0700 Subject: [PATCH 08/30] udpdate succinct json --- succinct.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/succinct.json b/succinct.json index 07db6d4b2..ec3467d06 100644 --- a/succinct.json +++ b/succinct.json @@ -5,14 +5,14 @@ "name": "evm", "framework": "plonky2x", "baseDir": ".", - "buildCommand": "mkdir build && cargo run --release --example evm build && mv ./target/release/examples/evm ./build/evm", + "buildCommand": "cargo run --release --example evm build && mv ./target/release/examples/evm ./build/evm", "proveCommand": "./build/evm prove input.json" }, { "name": "eth_call", "framework": "rustx", "baseDir": ".", - "buildCommand": "mkdir build && cargo run --release --example eth_call build && mv ./target/release/examples/eth_call ./build/eth_call", + "buildCommand": "cargo run --release --example eth_call build && mv ./target/release/examples/eth_call ./build/eth_call", "proveCommand": "./build/eth_call prove input.json" } ] From ec7908c57e7f3cc9dc310a2abfa2ec2dc7698cc4 Mon Sep 17 00:00:00 2001 From: Chris Tian Date: Thu, 12 Oct 2023 10:51:31 -0700 Subject: [PATCH 09/30] go mod tidy --- go.mod | 5 +---- go.sum | 48 +----------------------------------------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index 52699ef41..803a4f51f 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/consensys/gnark v0.9.0 github.com/consensys/gnark-crypto v0.12.1 github.com/ethereum/go-ethereum v1.12.0 - github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.4 + github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec ) require ( @@ -27,15 +27,12 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/zerolog v1.31.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/go.sum b/go.sum index 214632f89..4742a2aa7 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIO github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= -github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.9.0 h1:g1YivPG8jOtrN013Fe8OBXubkiTwvm7/vG2vXz03ANU= github.com/bits-and-blooms/bitset v1.9.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -20,18 +18,11 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark v0.8.1-0.20230803132917-bd4a39719a96 h1:BtDmnURu+BjTQjq0Yu5QIhMnu7BGXwoXEYggzgvFkNc= -github.com/consensys/gnark v0.8.1-0.20230803132917-bd4a39719a96/go.mod h1:Y70OuyMjYNvmTUL/sNKhehLOgByRYSOR8X6WMo7GmGQ= github.com/consensys/gnark v0.9.0 h1:OoOr0Q771mQINVdP3s1AF2Rs1y8gtXhWVkadz/9KmZc= github.com/consensys/gnark v0.9.0/go.mod h1:Sy9jJjIaGJFfNeupyNOR9Ei2IbAB6cfCO78DfG27YvM= -github.com/consensys/gnark-crypto v0.11.1-0.20230724160225-800ddb59f51b h1:tlxRLAcCOcdC3TcP5uqrKaK+OF7eBy1YB5AYUUhOayM= -github.com/consensys/gnark-crypto v0.11.1-0.20230724160225-800ddb59f51b/go.mod h1:6C2ytC8zmP8uH2GKVfPOjf0Vw3KwMAaUxlCPK5WQqmw= -github.com/consensys/gnark-crypto v0.11.2 h1:GJjjtWJ+db1xGao7vTsOgAOGgjfPe7eRGPL+xxMX0qE= -github.com/consensys/gnark-crypto v0.11.2/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= @@ -44,8 +35,6 @@ github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2 github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= @@ -60,10 +49,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/pprof v0.0.0-20230728192033-2ba5b33183c6 h1:ZgoomqkdjGbQ3+qQXCkvYMCDvGDNg2k5JJDjjdTB6jY= -github.com/google/pprof v0.0.0-20230728192033-2ba5b33183c6/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -75,18 +60,14 @@ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZ github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c h1:DZfsyhDK1hnSS5lH8l+JggqzEleHteTYfutAiVlSUM8= github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -104,26 +85,15 @@ github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= -github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20230724231837-7bd0035e65e3 h1:2UL4wMVXtuGnE7f+Bt7gTtMg3bU8Bh3xjNMiR7wzrT0= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20230724231837-7bd0035e65e3/go.mod h1:5jUXJKCi/CwL0U2YWvwKR39mvLH5yV9f7ql+skd0eC8= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012004323-13624e4daf03 h1:xgFRFTvL4357IAs/PE3OhouIL52jG+2WkV7GziiPdbQ= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012004323-13624e4daf03/go.mod h1:5jUXJKCi/CwL0U2YWvwKR39mvLH5yV9f7ql+skd0eC8= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec h1:xMlCVbabspLQ/sukwyu1WQzznh476x4O2/ee5X8igZ0= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -134,33 +104,18 @@ github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZF github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -168,7 +123,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= From d227b188ba68478745226bab3c615125fbb850d9 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 15:39:38 -0700 Subject: [PATCH 10/30] change to plonk --- plonky2x/.gitignore | 4 ++++ plonky2x/verifier/circuit.go | 17 +++++++++++------ plonky2x/verifier/cli.go | 10 +++++----- plonky2x/verifier/prover.go | 13 +++++++------ plonky2x/verifier/verifier.go | 12 ++++++------ 5 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 plonky2x/.gitignore diff --git a/plonky2x/.gitignore b/plonky2x/.gitignore new file mode 100644 index 000000000..19df4a90a --- /dev/null +++ b/plonky2x/.gitignore @@ -0,0 +1,4 @@ +verifier-build/ +core/wrapped/ +*tar.gz +verifier-build-groth16/ \ No newline at end of file diff --git a/plonky2x/verifier/circuit.go b/plonky2x/verifier/circuit.go index e1e0a89ce..d542d2311 100644 --- a/plonky2x/verifier/circuit.go +++ b/plonky2x/verifier/circuit.go @@ -7,11 +7,12 @@ import ( "time" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/logger" + "github.com/consensys/gnark/test" "github.com/succinctlabs/gnark-plonky2-verifier/types" "github.com/succinctlabs/gnark-plonky2-verifier/variables" "github.com/succinctlabs/gnark-plonky2-verifier/verifier" @@ -74,7 +75,7 @@ func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { return nil } -func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey, error) { +func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSystem, plonk.ProvingKey, plonk.VerifyingKey, error) { log := logger.Logger() verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), @@ -92,14 +93,18 @@ func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSyste OutputHash: new(frontend.Variable), CommonCircuitData: commonCircuitData, } - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) if err != nil { return nil, nil, nil, fmt.Errorf("failed to compile circuit: %w", err) } log.Info().Msg("Running circuit setup") start := time.Now() - pk, vk, err := groth16.Setup(r1cs) + srs, err := test.NewKZGSRS(r1cs) + if err != nil { + panic(err) + } + pk, vk, err := plonk.Setup(r1cs, srs) if err != nil { return nil, nil, nil, err } @@ -109,7 +114,7 @@ func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSyste return r1cs, pk, vk, nil } -func SaveVerifierCircuit(path string, r1cs constraint.ConstraintSystem, pk groth16.ProvingKey, vk groth16.VerifyingKey) error { +func SaveVerifierCircuit(path string, r1cs constraint.ConstraintSystem, pk plonk.ProvingKey, vk plonk.VerifyingKey) error { log := logger.Logger() os.MkdirAll(path, 0755) log.Info().Msg("Saving circuit constraints to " + path + "/r1cs.bin") diff --git a/plonky2x/verifier/cli.go b/plonky2x/verifier/cli.go index 9169349ca..3ad7a3866 100644 --- a/plonky2x/verifier/cli.go +++ b/plonky2x/verifier/cli.go @@ -5,7 +5,7 @@ import ( "flag" "os" - "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/logger" ) @@ -57,13 +57,13 @@ func main() { } if *proofFlag { - log.Info().Msg("loading the groth16 proving key and circuit data") + log.Info().Msg("loading the plonk proving key and circuit data") r1cs, pk, err := LoadProverData(*dataPath) if err != nil { log.Err(err).Msg("failed to load the verifier circuit") os.Exit(1) } - log.Info().Msg("creating the groth16 verifier proof") + log.Info().Msg("creating the plonk verifier proof") proof, publicWitness, err := Prove(*circuitPath, r1cs, pk) if err != nil { log.Err(err).Msg("failed to create the proof") @@ -76,7 +76,7 @@ func main() { log.Err(err).Msg("failed to load the verifier key") os.Exit(1) } - err = groth16.Verify(proof, vk, publicWitness) + err = plonk.Verify(proof, vk, publicWitness) if err != nil { log.Err(err).Msg("failed to verify proof") os.Exit(1) @@ -102,7 +102,7 @@ func main() { log.Err(err).Msg("failed to load the proof") os.Exit(1) } - err = groth16.Verify(proof, vk, publicWitness) + err = plonk.Verify(proof, vk, publicWitness) if err != nil { log.Err(err).Msg("failed to verify proof") os.Exit(1) diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index 72779486b..c43faa251 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -10,7 +10,8 @@ import ( "time" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" + "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" @@ -22,13 +23,13 @@ import ( "github.com/succinctlabs/sdk/gnarkx/types" ) -func LoadProverData(path string) (constraint.ConstraintSystem, groth16.ProvingKey, error) { +func LoadProverData(path string) (constraint.ConstraintSystem, plonk.ProvingKey, error) { log := logger.Logger() r1csFile, err := os.Open(path + "/r1cs.bin") if err != nil { return nil, nil, fmt.Errorf("failed to open r1cs file: %w", err) } - r1cs := groth16.NewCS(ecc.BN254) + r1cs := plonk.NewCS(ecc.BN254) start := time.Now() r1csReader := bufio.NewReader(r1csFile) _, err = r1cs.ReadFrom(r1csReader) @@ -43,7 +44,7 @@ func LoadProverData(path string) (constraint.ConstraintSystem, groth16.ProvingKe if err != nil { return nil, nil, fmt.Errorf("failed to open pk file: %w", err) } - pk := groth16.NewProvingKey(ecc.BN254) + pk := plonk.NewProvingKey(ecc.BN254) start = time.Now() pkReader := bufio.NewReader(pkFile) _, err = pk.ReadFrom(pkReader) @@ -77,7 +78,7 @@ func GetInputHashOutputHash(proofWithPis gnark_verifier_types.ProofWithPublicInp return inputHash, outputHash } -func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk groth16.ProvingKey) (groth16.Proof, witness.Witness, error) { +func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk plonk.ProvingKey) (plonk.Proof, witness.Witness, error) { log := logger.Logger() verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( @@ -108,7 +109,7 @@ func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk groth16.Prov log.Debug().Msg("Creating proof") start = time.Now() - proof, err := groth16.Prove(r1cs, pk, witness) + proof, err := plonk.Prove(r1cs, pk, witness) if err != nil { return nil, nil, fmt.Errorf("failed to create proof: %w", err) } diff --git a/plonky2x/verifier/verifier.go b/plonky2x/verifier/verifier.go index 651d8e896..c3cc693b5 100644 --- a/plonky2x/verifier/verifier.go +++ b/plonky2x/verifier/verifier.go @@ -10,18 +10,18 @@ import ( "time" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/logger" ) -func LoadVerifierKey(path string) (groth16.VerifyingKey, error) { +func LoadVerifierKey(path string) (plonk.VerifyingKey, error) { log := logger.Logger() vkFile, err := os.Open(path + "/vk.bin") if err != nil { return nil, fmt.Errorf("failed to open vk file: %w", err) } - vk := groth16.NewVerifyingKey(ecc.BN254) + vk := plonk.NewVerifyingKey(ecc.BN254) start := time.Now() _, err = vk.ReadFrom(vkFile) if err != nil { @@ -51,13 +51,13 @@ func LoadPublicWitness(circuitPath string) (witness.Witness, error) { return publicWitness, nil } -func LoadProof() (groth16.Proof, error) { +func LoadProof() (plonk.Proof, error) { log := logger.Logger() proofFile, err := os.Open("/proof.json") if err != nil { return nil, fmt.Errorf("failed to open proof file: %w", err) } - proof := groth16.NewProof(ecc.BN254) + proof := plonk.NewProof(ecc.BN254) jsonProof, err := io.ReadAll(proofFile) if err != nil { return nil, fmt.Errorf("failed to read proof file: %w", err) @@ -72,7 +72,7 @@ func LoadProof() (groth16.Proof, error) { return proof, nil } -func ExportIFunctionVerifierSolidity(path string, vk groth16.VerifyingKey) error { +func ExportIFunctionVerifierSolidity(path string, vk plonk.VerifyingKey) error { log := logger.Logger() // Create a new buffer and export the VerifyingKey into it as a Solidity contract and // convert the buffer content to a string for further manipulation. From d4998960816a2166314cb445fc97948d797099d4 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 18:49:09 -0700 Subject: [PATCH 11/30] Updates to work with new verifier --- assets/Verifier.sol | 1963 ++++++++++++++++++--- assets/Verifier_old.sol | 307 ++++ plonky2x/core/src/backend/function/mod.rs | 21 +- plonky2x/verifier/prover.go | 39 +- 4 files changed, 2014 insertions(+), 316 deletions(-) create mode 100644 assets/Verifier_old.sol diff --git a/assets/Verifier.sol b/assets/Verifier.sol index 78b271bbb..ab775197f 100644 --- a/assets/Verifier.sol +++ b/assets/Verifier.sol @@ -1,307 +1,1734 @@ +// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: AML +// Copyright 2023 Consensys Software Inc. // -// Copyright 2017 Christian Reitwiessner -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -// 2019 OKIMS - -pragma solidity ^0.8.16; - -library Pairing { - - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct G1Point { - uint256 X; - uint256 Y; - } +// 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. - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } +// Code generated by gnark DO NOT EDIT - /* - * @return The negation of p, i.e. p.plus(p.negate()) should be zero. - */ - function negate(G1Point memory p) internal pure returns (G1Point memory) { +pragma solidity ^0.8.19; - // The prime q in the base field F_q for G1 - if (p.X == 0 && p.Y == 0) { - return G1Point(0, 0); - } else { - return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); - } - } +contract PlonkVerifier { + uint256 private constant r_mod = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 private constant p_mod = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; - /* - * @return The sum of two points of G1 - */ - function plus( - G1Point memory p1, - G1Point memory p2 - ) internal view returns (G1Point memory r) { - - uint256[4] memory input; - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = p2.Y; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } + 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; - require(success,"pairing-add-failed"); - } + uint256 private constant g2_srs_1_x_0 = + 8092372495400684101491705897324646255244873767278146792539818137343983415338; + uint256 private constant g2_srs_1_x_1 = + 17267001818546224117620126850851175661030766706904745275133569110744678569705; + uint256 private constant g2_srs_1_y_0 = + 18322000439618828872938506585690152355173339417118410557175261086259665139770; + uint256 private constant g2_srs_1_y_1 = + 18361807460194502705595998955236116550325284436398091999656305930880342338897; + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 33554432; + uint256 private constant vk_inv_domain_size = + 21888242219518804655518433051623070663413851959604507555939307129453691614729; + uint256 private constant vk_omega = + 19200870435978225707111062059747084165650991997241425080699860725083300967194; + uint256 private constant vk_ql_com_x = + 10475058859656454050695647591133172935508386459718059756898410999806106551072; + uint256 private constant vk_ql_com_y = + 6213293053341394200050131794243928751814286941843296559783203899744980812514; + uint256 private constant vk_qr_com_x = + 7945139303378761555058466625722713607707726441206159286869501528418026434149; + uint256 private constant vk_qr_com_y = + 156590339282025629533899133000896162239385491129921159716602619014566566552; + uint256 private constant vk_qm_com_x = + 13992154963319852095604792769360675745515593362505622350879424943943884278192; + uint256 private constant vk_qm_com_y = + 20766012900377030563947776646862657527189870601611259870484355975445050379609; + uint256 private constant vk_qo_com_x = + 8470301768743093139793165421641707313403445331488881272110105479663725995698; + uint256 private constant vk_qo_com_y = + 21592895551100565151417052280131103082580614486310904268527641906481602947; + uint256 private constant vk_qk_com_x = + 5654499058770597633228728857640171762263824376475793805538552412908288004447; + uint256 private constant vk_qk_com_y = + 6419423782223253913146410978605915715045763868982098455567651000305029892860; - /* - * Same as plus but accepts raw input instead of struct - * @return The sum of two points of G1, one is represented as array - */ - function plus_raw(uint256[4] memory input, G1Point memory r) internal view { - bool success; + uint256 private constant vk_s1_com_x = + 3784233983189669014310841948904857803160351135759376541936942216013730316026; + uint256 private constant vk_s1_com_y = + 4129762303298768564961482720784423245788144573686711108417866217535238720827; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 {invalid()} - } + uint256 private constant vk_s2_com_x = + 14325725790615933789083728643797722641545097765592717304379756769414822396384; + uint256 private constant vk_s2_com_y = + 9206760818634532501699551379878233042314021497306309900329293412337859918890; - require(success, "pairing-add-failed"); - } + uint256 private constant vk_s3_com_x = + 1889439673607470886202316687133798310780298929819093754909820878439514588802; + uint256 private constant vk_s3_com_y = + 19385229208858775427536138610836306884212006605102248507339743025747156971303; - /* - * @return The product of a point on G1 and a scalar, i.e. - * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all - * points p. - */ - function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { - - uint256[3] memory input; - input[0] = p.X; - input[1] = p.Y; - input[2] = s; - bool success; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - require (success,"pairing-mul-failed"); - } + uint256 private constant vk_coset_shift = 5; + uint256 private constant vk_selector_commitments_commit_api_0_x = + 4117073030668173863289093683350495260595200061919445680132579228612965095242; + uint256 private constant vk_selector_commitments_commit_api_0_y = + 11919248650124787763761338505073673225954611652034365598154808475925452350203; - /* - * Same as scalar_mul but accepts raw input instead of struct, - * Which avoid extra allocation. provided input can be allocated outside and re-used multiple times - */ - function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view { - bool success; + uint256 private constant vk_index_commit_api_0 = 12269855; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 {invalid()} - } - require(success, "pairing-mul-failed"); - } + uint256 private constant vk_nb_commitments_commit_api = 1; - /* @return The result of computing the pairing check - * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 - * For example, - * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. - */ - function pairing( - G1Point memory a1, - G2Point memory a2, - G1Point memory b1, - G2Point memory b2, - G1Point memory c1, - G2Point memory c2, - G1Point memory d1, - G2Point memory d2 - ) internal view returns (bool) { - - G1Point[4] memory p1 = [a1, b1, c1, d1]; - G2Point[4] memory p2 = [a2, b2, c2, d2]; - uint256 inputSize = 24; - uint256[] memory input = new uint256[](inputSize); - - for (uint256 i = 0; i < 4; i++) { - uint256 j = i * 6; - input[j + 0] = p1[i].X; - input[j + 1] = p1[i].Y; - input[j + 2] = p2[i].X[0]; - input[j + 3] = p2[i].X[1]; - input[j + 4] = p2[i].Y[0]; - input[j + 5] = p2[i].Y[1]; - } + // ------------------------------------------------ - uint256[1] memory out; - bool success; + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } + // 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; - require(success,"pairing-opcode-failed"); + // 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; - return out[0] != 0; - } -} + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) -contract Verifier { + //Bn254.G1Point grand_product_commitment; // [z(x)] + uint256 private constant proof_grand_product_commitment_x = 0x220; + uint256 private constant proof_grand_product_commitment_y = 0x240; - using Pairing for *; + uint256 private constant proof_grand_product_at_zeta_omega = 0x260; // z(w*zeta) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - struct VerifyingKey { - Pairing.G1Point alfa1; - Pairing.G2Point beta2; - Pairing.G2Point gamma2; - Pairing.G2Point delta2; - // []G1Point IC (K in gnark) appears directly in verifyProof - } + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - struct Proof { - Pairing.G1Point A; - Pairing.G2Point B; - Pairing.G1Point C; - } + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> next part of proof is + // [ openings_selector_commits || commitments_wires_commit_api] - function verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.alfa1 = Pairing.G1Point(uint256(11020682126511168068160839679513889820813495861687678032140546067505531899092), uint256(5995328408262325819084541866941201935953243570305245558736929313802212001310)); - vk.beta2 = Pairing.G2Point([uint256(21692440125578184783857570388757267057140185352681860514052043427665453364801), uint256(15659896727025329910957273531332288720365326121006411758011965480663422924083)], [uint256(19028298600807183596556930202724519452796103375122979600632814652622994276436), uint256(19135704786862811638037330823020542674409825032148237442904765642474718994099)]); - vk.gamma2 = Pairing.G2Point([uint256(5366804362744839936515733233163925293622762608834071595895261894577612524603), uint256(13377946564735682308447861567067212951909700069657596665687636357849445285773)], [uint256(545228656900463501759782221345154972903251490355894212998066931928509461970), uint256(4475313117223647925880018085413127878343273821258184606834330199586258187690)]); - vk.delta2 = Pairing.G2Point([uint256(7040107673820945049142254998362889096779897272424860581697388639354731895760), uint256(275018460132017694013804586160820246090854203238980097454702642709574181670)], [uint256(17660627214538717600258808245166026882125201018137164375663395192013746255172), uint256(2378381866927591964998863532641261971962047219870673411534433987909560472428)]); - } + // -------- offset state + // challenges to check the claimed quotient + uint256 private constant state_alpha = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; - // accumulate scalarMul(mul_input) into q - // that is computes sets q = (mul_input[0:2] * mul_input[3]) + q - function accumulate( - uint256[3] memory mul_input, - Pairing.G1Point memory p, - uint256[4] memory buffer, - Pairing.G1Point memory q - ) internal view { - // computes p = mul_input[0:2] * mul_input[3] - Pairing.scalar_mul_raw(mul_input, p); - - // point addition inputs - buffer[0] = q.X; - buffer[1] = q.Y; - buffer[2] = p.X; - buffer[3] = p.Y; - - // q = p + q - Pairing.plus_raw(buffer, q); - } + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = + 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + // -------- utils (for hash_fr) + uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant zero_uint256 = 0; + + uint8 private constant lenInBytes = 48; + uint8 private constant sizeDomain = 11; + uint8 private constant one = 1; + uint8 private constant two = 2; - /* - * @returns Whether the proof is valid given the hardcoded verifying key - * above and the public inputs - */ function verifyProof( - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - uint256[3] calldata input - ) public view returns (bool r) { + bytes calldata proof, + uint256[] calldata public_inputs + ) public view returns (bool success) { + assembly { + let mem := mload(0x40) + let freeMem := add(mem, state_last_mem) - Proof memory proof; - proof.A = Pairing.G1Point(a[0], a[1]); - proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); - proof.C = Pairing.G1Point(c[0], c[1]); + // sanity checks + check_inputs_size(public_inputs.length, public_inputs.offset) + check_proof_size(proof.length) + check_proof_openings_size(proof.offset) - // Make sure that proof.A, B, and C are each less than the prime q - require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); - require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + // 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) - require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); - require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + // 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 + ) - require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); - require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + // public inputs contribution + let l_pi := sum_pi_wo_api_commit( + public_inputs.offset, + public_inputs.length, + freeMem + ) + let l_wocommit := sum_pi_commit( + proof.offset, + public_inputs.length, + freeMem + ) + l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(mem, state_pi), l_pi) - require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); - require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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) - // Make sure that every input is less than the snark scalar field - for (uint256 i = 0; i < input.length; i++) { - require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); - } + success := mload(add(mem, state_success)) + + // Beginning errors ------------------------------------------------- + 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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for { + let i + } lt(i, s) { + i := add(i, 1) + } { + input_checks := and( + input_checks, + lt(calldataload(p), r_mod) + ) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add( + 0x340, + mul(vk_nb_commitments_commit_api, 0x60) + ) + if iszero(eq(actual_proof_size, expected_proof_size)) { + error_proof_size() + } + } + + function check_proof_openings_size(aproof) { + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + openings_check := and( + openings_check, + lt(calldataload(p), r_mod) + ) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add( + aproof, + proof_openings_selector_commit_api_at_zeta + ) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul( + vk_nb_commitments_commit_api, + 0x40 + ) + calldatacopy( + _mPtr, + _proof, + size_wire_commitments_commit_api_in_bytes + ) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced) -> beta_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy( + add(mPtr, 0x40), + add(aproof, proof_grand_product_commitment_x), + 0x40 + ) + let l_success := staticcall( + gas(), + 0x2, + add(mPtr, 0x1b), + 0x65, + mPtr, + 0x20 + ) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + // mPtr free memory. Computes the public input contribution related to the commit + 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_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + 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) + } - VerifyingKey memory vk = verifyingKey(); - - // Compute the linear combination vk_x - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - - // Buffer reused for addition p1 + p2 to avoid memory allocations - // [0:2] -> p1.X, p1.Y ; [2:4] -> p2.X, p2.Y - uint256[4] memory add_input; - - // Buffer reused for multiplication p1 * s - // [0:2] -> p1.X, p1.Y ; [3] -> s - uint256[3] memory mul_input; - - // temporary point to avoid extra allocations in accumulate - Pairing.G1Point memory q = Pairing.G1Point(0, 0); - - vk_x.X = uint256(6166875854139708612521431984106466585837217993205781805493439401303829446279); // vk.K[0].X - vk_x.Y = uint256(5176386881937885470118103638958661997739679753062074457256694118824627869314); // vk.K[0].Y - mul_input[0] = uint256(0); // vk.K[1].X - mul_input[1] = uint256(0); // vk.K[1].Y - mul_input[2] = input[0]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[1] * input[0] - mul_input[0] = uint256(0); // vk.K[2].X - mul_input[1] = uint256(0); // vk.K[2].Y - mul_input[2] = input[1]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[2] * input[1] - mul_input[0] = uint256(0); // vk.K[3].X - mul_input[1] = uint256(0); // vk.K[3].Y - mul_input[2] = input[2]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[3] * input[2] - - return Pairing.pairing( - Pairing.negate(proof.A), - proof.B, - vk.alfa1, - vk.beta2, - vk_x, - vk.gamma2, - proof.C, - vk.delta2 - ); + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr) -> res { + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), 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), lenInBytes) + 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), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for { + let i := 0 + } lt(i, 0x10) { + i := add(i, 1) + } { // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall( + gas(), + 7, + folded_evals_commit, + 0x60, + folded_evals_commit, + 0x40 + ) + if eq(check_staticcall, 0) { + 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add( + 0x200, + mul(vk_nb_commitments_commit_api, 0x40) + ) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore( + add(state, state_folded_digests_x), + mload(add(mPtr, 0x40)) + ) + mstore( + add(state, state_folded_digests_y), + mload(add(mPtr, 0x60)) + ) + mstore( + add(state, state_folded_claimed_values), + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) + ) + + point_acc_mul( + add(state, state_folded_digests_x), + add(mPtr, 0x80), + acc_gamma, + mPtrOffset + ) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), + add(aproof, proof_linearised_polynomial_at_zeta), + acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul( + add(state, state_folded_digests_x), + add(mPtr, 0xc0), + acc_gamma, + mPtrOffset + ) + 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( + add(state, state_folded_digests_x), + add(mPtr, 0x100), + acc_gamma, + add(mPtr, offset) + ) + 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( + add(state, state_folded_digests_x), + add(mPtr, 0x140), + acc_gamma, + add(mPtr, offset) + ) + 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) + point_acc_mul( + add(state, state_folded_digests_x), + add(mPtr, 0x180), + acc_gamma, + add(mPtr, offset) + ) + 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) + point_acc_mul( + add(state, state_folded_digests_x), + add(mPtr, 0x1c0), + acc_gamma, + add(mPtr, offset) + ) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), + add(aproof, proof_s2_at_zeta), + acc_gamma + ) + + let poscaz := add( + aproof, + proof_openings_selector_commit_api_at_zeta + ) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul( + add(state, state_folded_digests_x), + opca, + acc_gamma, + add(mPtr, offset) + ) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), + poscaz, + acc_gamma + ) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) + mstore( + add(mPtr, 0x80), + mload(add(state, state_linearised_polynomial_x)) + ) + mstore( + add(mPtr, 0xa0), + mload(add(state, state_linearised_polynomial_y)) + ) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr, 0x180), vk_s1_com_x) + mstore(add(mPtr, 0x1a0), vk_s1_com_y) + mstore(add(mPtr, 0x1c0), vk_s2_com_x) + mstore(add(mPtr, 0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore( + add(mPtr, offset), + vk_selector_commitments_commit_api_0_x + ) + mstore( + add(mPtr, add(offset, 0x20)), + vk_selector_commitments_commit_api_0_y + ) + offset := add(offset, 0x40) + + mstore( + add(mPtr, offset), + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) + ) + mstore( + add(mPtr, add(offset, 0x20)), + calldataload( + add(aproof, proof_linearised_polynomial_at_zeta) + ) + ) + mstore( + add(mPtr, add(offset, 0x40)), + calldataload(add(aproof, proof_l_at_zeta)) + ) + mstore( + add(mPtr, add(offset, 0x60)), + calldataload(add(aproof, proof_r_at_zeta)) + ) + mstore( + add(mPtr, add(offset, 0x80)), + calldataload(add(aproof, proof_o_at_zeta)) + ) + mstore( + add(mPtr, add(offset, 0xa0)), + calldataload(add(aproof, proof_s1_at_zeta)) + ) + mstore( + add(mPtr, add(offset, 0xc0)), + calldataload(add(aproof, proof_s2_at_zeta)) + ) + + let _mPtr := add(mPtr, add(offset, 0xe0)) + let _poscaz := add( + aproof, + proof_openings_selector_commit_api_at_zeta + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + let start_input := 0x1b // 00.."gamma" + let size_input := add( + 0x16, + mul(vk_nb_commitments_commit_api, 3) + ) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { + 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 commits_api_at_zeta := add( + aproof, + proof_openings_selector_commit_api_at_zeta + ) + let commits_api := add( + aproof, + add( + proof_openings_selector_commit_api_at_zeta, + mul(vk_nb_commitments_commit_api, 0x20) + ) + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore( + add(mPtr, 0x20), + calldataload(add(commits_api, 0x20)) + ) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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) + ) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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 + ) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore( + s1, + mulmod( + calldataload(add(aproof, proof_s1_at_zeta)), + mload(add(state, state_beta)), + r_mod + ) + ) + mstore( + s1, + addmod(mload(s1), mload(add(state, state_gamma)), r_mod) + ) + mstore( + s1, + addmod( + mload(s1), + calldataload(add(aproof, proof_l_at_zeta)), + r_mod + ) + ) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore( + s2, + mulmod( + calldataload(add(aproof, proof_s2_at_zeta)), + mload(add(state, state_beta)), + r_mod + ) + ) + mstore( + s2, + addmod(mload(s2), mload(add(state, state_gamma)), r_mod) + ) + mstore( + s2, + addmod( + mload(s2), + calldataload(add(aproof, proof_r_at_zeta)), + r_mod + ) + ) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore( + o, + addmod( + calldataload(add(aproof, proof_o_at_zeta)), + mload(add(state, state_gamma)), + r_mod + ) + ) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore( + s1, + mulmod(mload(s1), mload(add(state, state_alpha)), r_mod) + ) + mstore( + s1, + mulmod( + mload(s1), + calldataload( + add(aproof, proof_grand_product_at_zeta_omega) + ), + r_mod + ) + ) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore( + computed_quotient, + addmod( + calldataload( + add(aproof, proof_linearised_polynomial_at_zeta) + ), + mload(add(state, state_pi)), + r_mod + ) + ) + mstore( + computed_quotient, + addmod(mload(computed_quotient), mload(s1), r_mod) + ) + mstore( + computed_quotient, + addmod( + mload(computed_quotient), + sub( + r_mod, + mload(add(state, state_alpha_square_lagrange_0)) + ), + r_mod + ) + ) + mstore( + s2, + mulmod( + calldataload( + add(aproof, proof_quotient_polynomial_at_zeta) + ), + mload(add(state, state_zeta_power_n_minus_one)), + r_mod + ) + ) + + mstore( + add(state, state_success), + eq(mload(computed_quotient), mload(s2)) + ) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + ) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40) + ) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(), + 0x05, + mPtr, + 0xc0, + mPtr, + 0x20 + ) + if eq(check_staticcall, 0) { + error_verify() + } + res := mload(mPtr) + } + } } } diff --git a/assets/Verifier_old.sol b/assets/Verifier_old.sol new file mode 100644 index 000000000..78b271bbb --- /dev/null +++ b/assets/Verifier_old.sol @@ -0,0 +1,307 @@ + +// SPDX-License-Identifier: AML +// +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.8.16; + +library Pairing { + + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus( + G1Point memory p1, + G1Point memory p2 + ) internal view returns (G1Point memory r) { + + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-add-failed"); + } + + + /* + * Same as plus but accepts raw input instead of struct + * @return The sum of two points of G1, one is represented as array + */ + function plus_raw(uint256[4] memory input, G1Point memory r) internal view { + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 {invalid()} + } + + require(success, "pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require (success,"pairing-mul-failed"); + } + + + /* + * Same as scalar_mul but accepts raw input instead of struct, + * Which avoid extra allocation. provided input can be allocated outside and re-used multiple times + */ + function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view { + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 {invalid()} + } + require(success, "pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-opcode-failed"); + + return out[0] != 0; + } +} + +contract Verifier { + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alfa1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + // []G1Point IC (K in gnark) appears directly in verifyProof + } + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alfa1 = Pairing.G1Point(uint256(11020682126511168068160839679513889820813495861687678032140546067505531899092), uint256(5995328408262325819084541866941201935953243570305245558736929313802212001310)); + vk.beta2 = Pairing.G2Point([uint256(21692440125578184783857570388757267057140185352681860514052043427665453364801), uint256(15659896727025329910957273531332288720365326121006411758011965480663422924083)], [uint256(19028298600807183596556930202724519452796103375122979600632814652622994276436), uint256(19135704786862811638037330823020542674409825032148237442904765642474718994099)]); + vk.gamma2 = Pairing.G2Point([uint256(5366804362744839936515733233163925293622762608834071595895261894577612524603), uint256(13377946564735682308447861567067212951909700069657596665687636357849445285773)], [uint256(545228656900463501759782221345154972903251490355894212998066931928509461970), uint256(4475313117223647925880018085413127878343273821258184606834330199586258187690)]); + vk.delta2 = Pairing.G2Point([uint256(7040107673820945049142254998362889096779897272424860581697388639354731895760), uint256(275018460132017694013804586160820246090854203238980097454702642709574181670)], [uint256(17660627214538717600258808245166026882125201018137164375663395192013746255172), uint256(2378381866927591964998863532641261971962047219870673411534433987909560472428)]); + } + + + // accumulate scalarMul(mul_input) into q + // that is computes sets q = (mul_input[0:2] * mul_input[3]) + q + function accumulate( + uint256[3] memory mul_input, + Pairing.G1Point memory p, + uint256[4] memory buffer, + Pairing.G1Point memory q + ) internal view { + // computes p = mul_input[0:2] * mul_input[3] + Pairing.scalar_mul_raw(mul_input, p); + + // point addition inputs + buffer[0] = q.X; + buffer[1] = q.Y; + buffer[2] = p.X; + buffer[3] = p.Y; + + // q = p + q + Pairing.plus_raw(buffer, q); + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[3] calldata input + ) public view returns (bool r) { + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + for (uint256 i = 0; i < input.length; i++) { + require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); + } + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Buffer reused for addition p1 + p2 to avoid memory allocations + // [0:2] -> p1.X, p1.Y ; [2:4] -> p2.X, p2.Y + uint256[4] memory add_input; + + // Buffer reused for multiplication p1 * s + // [0:2] -> p1.X, p1.Y ; [3] -> s + uint256[3] memory mul_input; + + // temporary point to avoid extra allocations in accumulate + Pairing.G1Point memory q = Pairing.G1Point(0, 0); + + vk_x.X = uint256(6166875854139708612521431984106466585837217993205781805493439401303829446279); // vk.K[0].X + vk_x.Y = uint256(5176386881937885470118103638958661997739679753062074457256694118824627869314); // vk.K[0].Y + mul_input[0] = uint256(0); // vk.K[1].X + mul_input[1] = uint256(0); // vk.K[1].Y + mul_input[2] = input[0]; + accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[1] * input[0] + mul_input[0] = uint256(0); // vk.K[2].X + mul_input[1] = uint256(0); // vk.K[2].Y + mul_input[2] = input[1]; + accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[2] * input[1] + mul_input[0] = uint256(0); // vk.K[3].X + mul_input[1] = uint256(0); // vk.K[3].Y + mul_input[2] = input[2]; + accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[3] * input[2] + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alfa1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 545aac899..caf5f947e 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -244,9 +244,8 @@ impl Plonky2xFunction for C { } fn verifier(circuit_digest: &str) -> String { - let generated_contract = VERIFIER_CONTRACT - .replace("pragma solidity ^0.8.0;", "pragma solidity ^0.8.16;") - .replace("uint256[3] calldata input", "uint256[3] memory input"); + let generated_contract = + VERIFIER_CONTRACT.replace("pragma solidity ^0.8.0;", "pragma solidity ^0.8.16;"); let verifier_contract = " @@ -256,23 +255,21 @@ interface IFunctionVerifier { function verificationKeyHash() external pure returns (bytes32); } -contract FunctionVerifier is IFunctionVerifier, Verifier { +contract FunctionVerifier is IFunctionVerifier, PlonkVerifier { bytes32 public constant CIRCUIT_DIGEST = {CIRCUIT_DIGEST}; function verify(bytes32 _inputHash, bytes32 _outputHash, bytes memory _proof) external view returns (bool) { - (uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) = - abi.decode(_proof, (uint256[2], uint256[2][2], uint256[2])); + uint256[] memory input = new uint256[](3); + input[0] = uint256(CIRCUIT_DIGEST); + input[1] = uint256(_inputHash) & ((1 << 253) - 1); + input[2] = uint256(_outputHash) & ((1 << 253) - 1); - uint256[3] memory input = [uint256(CIRCUIT_DIGEST), uint256(_inputHash), uint256(_outputHash)]; - input[1] = input[1] & ((1 << 253) - 1); - input[2] = input[2] & ((1 << 253) - 1); - - return verifyProof(a, b, c, input); + return verifyProof(proof, input); } function verificationKeyHash() external pure returns (bytes32) { - return keccak256(abi.encode(verifyingKey())); + return CIRCUIT_DIGEST; } } ".replace("{CIRCUIT_DIGEST}", circuit_digest); diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index c43faa251..18a9e6767 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -2,7 +2,6 @@ package main import ( "bufio" - "bytes" "encoding/json" "fmt" "math/big" @@ -11,12 +10,12 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/plonk" + plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/logger" - "github.com/ethereum/go-ethereum/accounts/abi" gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" "github.com/succinctlabs/gnark-plonky2-verifier/variables" @@ -116,44 +115,12 @@ func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk plonk.Provin elapsed = time.Since(start) log.Info().Msg("Successfully created proof, time: " + elapsed.String()) - const fpSize = 4 * 8 - var buf bytes.Buffer - proof.WriteRawTo(&buf) - proofBytes := buf.Bytes() - output := &types.Groth16Proof{} - output.A[0] = new(big.Int).SetBytes(proofBytes[fpSize*0 : fpSize*1]) - output.A[1] = new(big.Int).SetBytes(proofBytes[fpSize*1 : fpSize*2]) - output.B[0][0] = new(big.Int).SetBytes(proofBytes[fpSize*2 : fpSize*3]) - output.B[0][1] = new(big.Int).SetBytes(proofBytes[fpSize*3 : fpSize*4]) - output.B[1][0] = new(big.Int).SetBytes(proofBytes[fpSize*4 : fpSize*5]) - output.B[1][1] = new(big.Int).SetBytes(proofBytes[fpSize*5 : fpSize*6]) - output.C[0] = new(big.Int).SetBytes(proofBytes[fpSize*6 : fpSize*7]) - output.C[1] = new(big.Int).SetBytes(proofBytes[fpSize*7 : fpSize*8]) - - // abi.encode(proof.A, proof.B, proof.C) - uint256Array, err := abi.NewType("uint256[2]", "", nil) - if err != nil { - log.Fatal().AnErr("Failed to create uint256[2] type", err) - } - uint256ArrayArray, err := abi.NewType("uint256[2][2]", "", nil) - if err != nil { - log.Fatal().AnErr("Failed to create uint256[2][2] type", err) - } - args := abi.Arguments{ - {Type: uint256Array}, - {Type: uint256ArrayArray}, - {Type: uint256Array}, - } - encodedProofBytes, err := args.Pack(output.A, output.B, output.C) - if err != nil { - log.Fatal().AnErr("Failed to encode proof", err) - } - + _proof := proof.(*plonk_bn254.Proof) log.Info().Msg("Saving proof to proof.json") jsonProof, err := json.Marshal(types.ProofResult{ // Output will be filled in by plonky2x CLI Output: []byte{}, - Proof: encodedProofBytes, + Proof: _proof.MarshalSolidity(), }) if err != nil { return nil, nil, fmt.Errorf("failed to marshal proof: %w", err) From 57d4e8de2cabd0462e4f9f5faeefeef884fe545b Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 19:58:56 -0700 Subject: [PATCH 12/30] Solidty version --- assets/Verifier.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/Verifier.sol b/assets/Verifier.sol index ab775197f..5d7cec7e1 100644 --- a/assets/Verifier.sol +++ b/assets/Verifier.sol @@ -16,7 +16,7 @@ // Code generated by gnark DO NOT EDIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.16; contract PlonkVerifier { uint256 private constant r_mod = @@ -819,7 +819,8 @@ contract PlonkVerifier { let i := 0 } lt(i, 0x10) { i := add(i, 1) - } { // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + } { + // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] mstore8(offset, 0x00) offset := add(offset, 0x1) } From 587a3f976f1df93150b4bb7db17c048193d81d4f Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 20:04:39 -0700 Subject: [PATCH 13/30] anotehr try --- assets/Verifier.sol | 2882 +++++++++------------ plonky2x/core/src/backend/function/mod.rs | 7 +- 2 files changed, 1174 insertions(+), 1715 deletions(-) diff --git a/assets/Verifier.sol b/assets/Verifier.sol index 5d7cec7e1..cb36ae081 100644 --- a/assets/Verifier.sol +++ b/assets/Verifier.sol @@ -16,1720 +16,1178 @@ // Code generated by gnark DO NOT EDIT -pragma solidity ^0.8.16; +pragma solidity ^0.8.19; contract PlonkVerifier { - uint256 private constant r_mod = - 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = - 8092372495400684101491705897324646255244873767278146792539818137343983415338; - uint256 private constant g2_srs_1_x_1 = - 17267001818546224117620126850851175661030766706904745275133569110744678569705; - uint256 private constant g2_srs_1_y_0 = - 18322000439618828872938506585690152355173339417118410557175261086259665139770; - uint256 private constant g2_srs_1_y_1 = - 18361807460194502705595998955236116550325284436398091999656305930880342338897; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 33554432; - uint256 private constant vk_inv_domain_size = - 21888242219518804655518433051623070663413851959604507555939307129453691614729; - uint256 private constant vk_omega = - 19200870435978225707111062059747084165650991997241425080699860725083300967194; - uint256 private constant vk_ql_com_x = - 10475058859656454050695647591133172935508386459718059756898410999806106551072; - uint256 private constant vk_ql_com_y = - 6213293053341394200050131794243928751814286941843296559783203899744980812514; - uint256 private constant vk_qr_com_x = - 7945139303378761555058466625722713607707726441206159286869501528418026434149; - uint256 private constant vk_qr_com_y = - 156590339282025629533899133000896162239385491129921159716602619014566566552; - uint256 private constant vk_qm_com_x = - 13992154963319852095604792769360675745515593362505622350879424943943884278192; - uint256 private constant vk_qm_com_y = - 20766012900377030563947776646862657527189870601611259870484355975445050379609; - uint256 private constant vk_qo_com_x = - 8470301768743093139793165421641707313403445331488881272110105479663725995698; - uint256 private constant vk_qo_com_y = - 21592895551100565151417052280131103082580614486310904268527641906481602947; - uint256 private constant vk_qk_com_x = - 5654499058770597633228728857640171762263824376475793805538552412908288004447; - uint256 private constant vk_qk_com_y = - 6419423782223253913146410978605915715045763868982098455567651000305029892860; - - uint256 private constant vk_s1_com_x = - 3784233983189669014310841948904857803160351135759376541936942216013730316026; - uint256 private constant vk_s1_com_y = - 4129762303298768564961482720784423245788144573686711108417866217535238720827; - - uint256 private constant vk_s2_com_x = - 14325725790615933789083728643797722641545097765592717304379756769414822396384; - uint256 private constant vk_s2_com_y = - 9206760818634532501699551379878233042314021497306309900329293412337859918890; - - uint256 private constant vk_s3_com_x = - 1889439673607470886202316687133798310780298929819093754909820878439514588802; - uint256 private constant vk_s3_com_y = - 19385229208858775427536138610836306884212006605102248507339743025747156971303; - - uint256 private constant vk_coset_shift = 5; - - uint256 private constant vk_selector_commitments_commit_api_0_x = - 4117073030668173863289093683350495260595200061919445680132579228612965095242; - uint256 private constant vk_selector_commitments_commit_api_0_y = - 11919248650124787763761338505073673225954611652034365598154808475925452350203; - - uint256 private constant vk_index_commit_api_0 = 12269855; - - uint256 private constant vk_nb_commitments_commit_api = 1; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = - 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - // -------- utils (for hash_fr) - uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 - uint256 private constant zero_uint256 = 0; - - uint8 private constant lenInBytes = 48; - uint8 private constant sizeDomain = 11; - uint8 private constant one = 1; - uint8 private constant two = 2; - - function verifyProof( - 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_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_wocommit := sum_pi_commit( - proof.offset, - public_inputs.length, - freeMem - ) - l_pi := addmod(l_wocommit, l_pi, r_mod) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for { - let i - } lt(i, s) { - i := add(i, 1) - } { - input_checks := and( - input_checks, - lt(calldataload(p), r_mod) - ) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add( - 0x340, - mul(vk_nb_commitments_commit_api, 0x60) - ) - if iszero(eq(actual_proof_size, expected_proof_size)) { - error_proof_size() - } - } - - function check_proof_openings_size(aproof) { - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - openings_check := and( - openings_check, - lt(calldataload(p), r_mod) - ) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add( - aproof, - proof_openings_selector_commit_api_at_zeta - ) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul( - vk_nb_commitments_commit_api, - 0x40 - ) - calldatacopy( - _mPtr, - _proof, - size_wire_commitments_commit_api_in_bytes - ) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced) -> beta_not_reduced { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy( - add(mPtr, 0x40), - add(aproof, proof_grand_product_commitment_x), - 0x40 - ) - let l_success := staticcall( - gas(), - 0x2, - add(mPtr, 0x1b), - 0x65, - mPtr, - 0x20 - ) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - // mPtr free memory. Computes the public input contribution related to the commit - 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_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - 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) - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr) -> res { - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), 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), lenInBytes) - 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), sizeDomain) - - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for { - let i := 0 - } lt(i, 0x10) { - i := add(i, 1) - } { - // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - } - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall( - gas(), - 7, - folded_evals_commit, - 0x60, - folded_evals_commit, - 0x40 - ) - if eq(check_staticcall, 0) { - 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add( - 0x200, - mul(vk_nb_commitments_commit_api, 0x40) - ) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore( - add(state, state_folded_digests_x), - mload(add(mPtr, 0x40)) - ) - mstore( - add(state, state_folded_digests_y), - mload(add(mPtr, 0x60)) - ) - mstore( - add(state, state_folded_claimed_values), - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) - ) - - point_acc_mul( - add(state, state_folded_digests_x), - add(mPtr, 0x80), - acc_gamma, - mPtrOffset - ) - fr_acc_mul_calldata( - add(state, state_folded_claimed_values), - add(aproof, proof_linearised_polynomial_at_zeta), - acc_gamma - ) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul( - add(state, state_folded_digests_x), - add(mPtr, 0xc0), - acc_gamma, - mPtrOffset - ) - 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( - add(state, state_folded_digests_x), - add(mPtr, 0x100), - acc_gamma, - add(mPtr, offset) - ) - 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( - add(state, state_folded_digests_x), - add(mPtr, 0x140), - acc_gamma, - add(mPtr, offset) - ) - 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) - point_acc_mul( - add(state, state_folded_digests_x), - add(mPtr, 0x180), - acc_gamma, - add(mPtr, offset) - ) - 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) - point_acc_mul( - add(state, state_folded_digests_x), - add(mPtr, 0x1c0), - acc_gamma, - add(mPtr, offset) - ) - fr_acc_mul_calldata( - add(state, state_folded_claimed_values), - add(aproof, proof_s2_at_zeta), - acc_gamma - ) - - let poscaz := add( - aproof, - proof_openings_selector_commit_api_at_zeta - ) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul( - add(state, state_folded_digests_x), - opca, - acc_gamma, - add(mPtr, offset) - ) - fr_acc_mul_calldata( - add(state, state_folded_claimed_values), - poscaz, - acc_gamma - ) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) - mstore( - add(mPtr, 0x80), - mload(add(state, state_linearised_polynomial_x)) - ) - mstore( - add(mPtr, 0xa0), - mload(add(state, state_linearised_polynomial_y)) - ) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr, 0x180), vk_s1_com_x) - mstore(add(mPtr, 0x1a0), vk_s1_com_y) - mstore(add(mPtr, 0x1c0), vk_s2_com_x) - mstore(add(mPtr, 0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore( - add(mPtr, offset), - vk_selector_commitments_commit_api_0_x - ) - mstore( - add(mPtr, add(offset, 0x20)), - vk_selector_commitments_commit_api_0_y - ) - offset := add(offset, 0x40) - - mstore( - add(mPtr, offset), - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) - ) - mstore( - add(mPtr, add(offset, 0x20)), - calldataload( - add(aproof, proof_linearised_polynomial_at_zeta) - ) - ) - mstore( - add(mPtr, add(offset, 0x40)), - calldataload(add(aproof, proof_l_at_zeta)) - ) - mstore( - add(mPtr, add(offset, 0x60)), - calldataload(add(aproof, proof_r_at_zeta)) - ) - mstore( - add(mPtr, add(offset, 0x80)), - calldataload(add(aproof, proof_o_at_zeta)) - ) - mstore( - add(mPtr, add(offset, 0xa0)), - calldataload(add(aproof, proof_s1_at_zeta)) - ) - mstore( - add(mPtr, add(offset, 0xc0)), - calldataload(add(aproof, proof_s2_at_zeta)) - ) - - let _mPtr := add(mPtr, add(offset, 0xe0)) - let _poscaz := add( - aproof, - proof_openings_selector_commit_api_at_zeta - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - let start_input := 0x1b // 00.."gamma" - let size_input := add( - 0x16, - mul(vk_nb_commitments_commit_api, 3) - ) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { - 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 commits_api_at_zeta := add( - aproof, - proof_openings_selector_commit_api_at_zeta - ) - let commits_api := add( - aproof, - add( - proof_openings_selector_commit_api_at_zeta, - mul(vk_nb_commitments_commit_api, 0x20) - ) - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore( - add(mPtr, 0x20), - calldataload(add(commits_api, 0x20)) - ) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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) - ) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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 - ) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore( - s1, - mulmod( - calldataload(add(aproof, proof_s1_at_zeta)), - mload(add(state, state_beta)), - r_mod - ) - ) - mstore( - s1, - addmod(mload(s1), mload(add(state, state_gamma)), r_mod) - ) - mstore( - s1, - addmod( - mload(s1), - calldataload(add(aproof, proof_l_at_zeta)), - r_mod - ) - ) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore( - s2, - mulmod( - calldataload(add(aproof, proof_s2_at_zeta)), - mload(add(state, state_beta)), - r_mod - ) - ) - mstore( - s2, - addmod(mload(s2), mload(add(state, state_gamma)), r_mod) - ) - mstore( - s2, - addmod( - mload(s2), - calldataload(add(aproof, proof_r_at_zeta)), - r_mod - ) - ) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore( - o, - addmod( - calldataload(add(aproof, proof_o_at_zeta)), - mload(add(state, state_gamma)), - r_mod - ) - ) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore( - s1, - mulmod(mload(s1), mload(add(state, state_alpha)), r_mod) - ) - mstore( - s1, - mulmod( - mload(s1), - calldataload( - add(aproof, proof_grand_product_at_zeta_omega) - ), - r_mod - ) - ) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore( - computed_quotient, - addmod( - calldataload( - add(aproof, proof_linearised_polynomial_at_zeta) - ), - mload(add(state, state_pi)), - r_mod - ) - ) - mstore( - computed_quotient, - addmod(mload(computed_quotient), mload(s1), r_mod) - ) - mstore( - computed_quotient, - addmod( - mload(computed_quotient), - sub( - r_mod, - mload(add(state, state_alpha_square_lagrange_0)) - ), - r_mod - ) - ) - mstore( - s2, - mulmod( - calldataload( - add(aproof, proof_quotient_polynomial_at_zeta) - ), - mload(add(state, state_zeta_power_n_minus_one)), - r_mod - ) - ) - - mstore( - add(state, state_success), - eq(mload(computed_quotient), mload(s2)) - ) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - ) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40) - ) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(), - 0x05, - mPtr, - 0xc0, - mPtr, - 0x20 - ) - if eq(check_staticcall, 0) { - error_verify() - } - res := mload(mPtr) - } + + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 8092372495400684101491705897324646255244873767278146792539818137343983415338; + uint256 private constant g2_srs_1_x_1 = 17267001818546224117620126850851175661030766706904745275133569110744678569705; + uint256 private constant g2_srs_1_y_0 = 18322000439618828872938506585690152355173339417118410557175261086259665139770; + uint256 private constant g2_srs_1_y_1 = 18361807460194502705595998955236116550325284436398091999656305930880342338897; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 33554432; + uint256 private constant vk_inv_domain_size = 21888242219518804655518433051623070663413851959604507555939307129453691614729; + uint256 private constant vk_omega = 19200870435978225707111062059747084165650991997241425080699860725083300967194; + uint256 private constant vk_ql_com_x = 10475058859656454050695647591133172935508386459718059756898410999806106551072; + uint256 private constant vk_ql_com_y = 6213293053341394200050131794243928751814286941843296559783203899744980812514; + uint256 private constant vk_qr_com_x = 7945139303378761555058466625722713607707726441206159286869501528418026434149; + uint256 private constant vk_qr_com_y = 156590339282025629533899133000896162239385491129921159716602619014566566552; + uint256 private constant vk_qm_com_x = 13992154963319852095604792769360675745515593362505622350879424943943884278192; + uint256 private constant vk_qm_com_y = 20766012900377030563947776646862657527189870601611259870484355975445050379609; + uint256 private constant vk_qo_com_x = 8470301768743093139793165421641707313403445331488881272110105479663725995698; + uint256 private constant vk_qo_com_y = 21592895551100565151417052280131103082580614486310904268527641906481602947; + uint256 private constant vk_qk_com_x = 5654499058770597633228728857640171762263824376475793805538552412908288004447; + uint256 private constant vk_qk_com_y = 6419423782223253913146410978605915715045763868982098455567651000305029892860; + + uint256 private constant vk_s1_com_x = 3784233983189669014310841948904857803160351135759376541936942216013730316026; + uint256 private constant vk_s1_com_y = 4129762303298768564961482720784423245788144573686711108417866217535238720827; + + uint256 private constant vk_s2_com_x = 14325725790615933789083728643797722641545097765592717304379756769414822396384; + uint256 private constant vk_s2_com_y = 9206760818634532501699551379878233042314021497306309900329293412337859918890; + + uint256 private constant vk_s3_com_x = 1889439673607470886202316687133798310780298929819093754909820878439514588802; + uint256 private constant vk_s3_com_y = 19385229208858775427536138610836306884212006605102248507339743025747156971303; + + uint256 private constant vk_coset_shift = 5; + + + uint256 private constant vk_selector_commitments_commit_api_0_x = 4117073030668173863289093683350495260595200061919445680132579228612965095242; + uint256 private constant vk_selector_commitments_commit_api_0_y = 11919248650124787763761338505073673225954611652034365598154808475925452350203; + + + uint256 private constant vk_index_commit_api_0 = 12269855; + + + uint256 private constant vk_nb_commitments_commit_api = 1; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + + // -------- utils (for hash_fr) + uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant zero_uint256 = 0; + + uint8 private constant lenInBytes = 48; + uint8 private constant sizeDomain = 11; + uint8 private constant one = 1; + uint8 private constant two = 2; + + + 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { + error_proof_size() + } + } + + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + + // mPtr free memory. Computes the public input contribution related to the commit + 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_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) + + + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + 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) + + } + + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr)->res { + + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), 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), lenInBytes) + 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), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { + 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} + { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr,0x180), vk_s1_com_x) + mstore(add(mPtr,0x1a0), vk_s1_com_y) + mstore(add(mPtr,0x1c0), vk_s2_com_x) + mstore(add(mPtr,0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) + mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) + offset := add(offset, 0x40) + + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + + let _mPtr := add(mPtr, add(offset, 0xe0)) + let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { + 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := add( + aproof, + add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) + mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() } + res := mload(mPtr) + } } + } } diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index caf5f947e..29f3fab5c 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -244,8 +244,9 @@ impl Plonky2xFunction for C { } fn verifier(circuit_digest: &str) -> String { - let generated_contract = - VERIFIER_CONTRACT.replace("pragma solidity ^0.8.0;", "pragma solidity ^0.8.16;"); + let generated_contract = VERIFIER_CONTRACT + .replace("pragma solidity ^0.8.19;", "pragma solidity ^0.8.16;") + .replace("function Verify", "function verifyProof"); let verifier_contract = " @@ -265,7 +266,7 @@ contract FunctionVerifier is IFunctionVerifier, PlonkVerifier { input[1] = uint256(_inputHash) & ((1 << 253) - 1); input[2] = uint256(_outputHash) & ((1 << 253) - 1); - return verifyProof(proof, input); + return verifyProof(_proof, input); } function verificationKeyHash() external pure returns (bytes32) { From ffccb04ec68112b6d68046d8fcd438cdbf9f100a Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Thu, 12 Oct 2023 20:14:38 -0700 Subject: [PATCH 14/30] cursed --- plonky2x/core/src/backend/function/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 29f3fab5c..6af929f63 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -266,7 +266,7 @@ contract FunctionVerifier is IFunctionVerifier, PlonkVerifier { input[1] = uint256(_inputHash) & ((1 << 253) - 1); input[2] = uint256(_outputHash) & ((1 << 253) - 1); - return verifyProof(_proof, input); + return this.verifyProof(_proof, input); } function verificationKeyHash() external pure returns (bytes32) { From 8af64bef8493fea6fa485e9ebe46b0e131463b2e Mon Sep 17 00:00:00 2001 From: puma314 Date: Fri, 13 Oct 2023 00:11:30 -0700 Subject: [PATCH 15/30] benchmarking code (#236) --- contracts/test/verifiers/TestVerifier.t.sol | 75 ++ contracts/test/verifiers/VerifierGroth16.sol | 546 ++++++++ contracts/test/verifiers/VerifierPlonk.sol | 1021 ++++++++++++++ .../verifiers/VerifierPlonkRangeCheck.sol | 1193 +++++++++++++++++ .../test/verifiers/groth16_proof_data.json | 17 + .../test/verifiers/plonk_proof_data.json | 8 + .../plonk_proof_data_range_check.json | 8 + plonky2x/verifier/gnark_verifiers_test.go | 228 ++++ 8 files changed, 3096 insertions(+) create mode 100644 contracts/test/verifiers/TestVerifier.t.sol create mode 100644 contracts/test/verifiers/VerifierGroth16.sol create mode 100644 contracts/test/verifiers/VerifierPlonk.sol create mode 100644 contracts/test/verifiers/VerifierPlonkRangeCheck.sol create mode 100644 contracts/test/verifiers/groth16_proof_data.json create mode 100644 contracts/test/verifiers/plonk_proof_data.json create mode 100644 contracts/test/verifiers/plonk_proof_data_range_check.json create mode 100644 plonky2x/verifier/gnark_verifiers_test.go diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol new file mode 100644 index 000000000..6a702e48f --- /dev/null +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +import "forge-std/Vm.sol"; +import "forge-std/console.sol"; +import "forge-std/Test.sol"; + +import {Verifier as Groth16Verifier} from "./VerifierGroth16.sol"; +import {PlonkVerifier} from "./VerifierPlonk.sol"; +import {PlonkVerifier as PlonkRangeCheckVerifier} from "./VerifierPlonkRangeCheck.sol"; +import {VmSafe} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +contract VerifierTest is Test { + function testVerifierGroth16() public { + Groth16Verifier verifier = new Groth16Verifier(); + + string memory groth16Json = vm.readFile( + "test/verifiers/groth16_proof_data.json" + ); + uint256[] memory proof = stdJson.readUintArray(groth16Json, "$.proof"); + uint256[] memory input = stdJson.readUintArray(groth16Json, "$.inputs"); + + uint256[8] memory proofConverted; + for (uint256 i = 0; i < 8; i++) { + proofConverted[i] = uint256(proof[i]); + } + + uint256[3] memory inputConverted; + for (uint256 i = 0; i < 3; i++) { + inputConverted[i] = uint256(input[i]); + } + uint256 startGas = gasleft(); + verifier.verifyProof(proofConverted, inputConverted); + uint256 endGas = gasleft(); + console.log("gas used: %d", startGas - endGas); + + uint256[4] memory compressedProof = verifier.compressProof( + proofConverted + ); + startGas = gasleft(); + verifier.verifyCompressedProof(compressedProof, inputConverted); + endGas = gasleft(); + console.log( + "gas used for verifying compressed proof: %d", + startGas - endGas + ); + } + + function testVerifierPlonk() public { + PlonkVerifier verifier = new PlonkVerifier(); + string memory proofJson = vm.readFile( + "test/verifiers/plonk_proof_data.json" + ); + bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); + uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); + uint256 startGas = gasleft(); + verifier.Verify(proof, input); + uint256 endGas = gasleft(); + console.log("gas used: %d", startGas - endGas); + } + + function testVerifierPlonkRangeCheck() public { + PlonkRangeCheckVerifier verifier = new PlonkRangeCheckVerifier(); + string memory proofJson = vm.readFile( + "test/verifiers/plonk_proof_data_range_check.json" + ); + bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); + uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); + uint256 startGas = gasleft(); + verifier.Verify(proof, input); + uint256 endGas = gasleft(); + console.log("gas used: %d", startGas - endGas); + } +} diff --git a/contracts/test/verifiers/VerifierGroth16.sol b/contracts/test/verifiers/VerifierGroth16.sol new file mode 100644 index 000000000..6cac2b170 --- /dev/null +++ b/contracts/test/verifiers/VerifierGroth16.sol @@ -0,0 +1,546 @@ + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @title Groth16 verifier template. +/// @author Remco Bloemen +/// @notice Supports verifying Groth16 proofs. Proofs can be in uncompressed +/// (256 bytes) and compressed (128 bytes) format. A view function is provided +/// to compress proofs. +/// @notice See for further explanation. +contract Verifier { + + /// Some of the provided public input values are larger than the field modulus. + /// @dev Public input elements are not automatically reduced, as this is can be + /// a dangerous source of bugs. + error PublicInputNotInField(); + + /// The proof is invalid. + /// @dev This can mean that provided Groth16 proof points are not on their + /// curves, that pairing equation fails, or that the proof is not for the + /// provided public input. + error ProofInvalid(); + + // Addresses of precompiles + uint256 constant PRECOMPILE_MODEXP = 0x05; + uint256 constant PRECOMPILE_ADD = 0x06; + uint256 constant PRECOMPILE_MUL = 0x07; + uint256 constant PRECOMPILE_VERIFY = 0x08; + + // Base field Fp order P and scalar field Fr order R. + // For BN254 these are computed as follows: + // t = 4965661367192848881 + // P = 36⋅t⁴ + 36⋅t³ + 24⋅t² + 6⋅t + 1 + // R = 36⋅t⁴ + 36⋅t³ + 18⋅t² + 6⋅t + 1 + uint256 constant P = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 constant R = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; + + // Extension field Fp2 = Fp[i] / (i² + 1) + // Note: This is the complex extension field of Fp with i² = -1. + // Values in Fp2 are represented as a pair of Fp elements (a₀, a₁) as a₀ + a₁⋅i. + // Note: The order of Fp2 elements is *opposite* that of the pairing contract, which + // expects Fp2 elements in order (a₁, a₀). This is also the order in which + // Fp2 elements are encoded in the public interface as this became convention. + + // Constants in Fp + uint256 constant FRACTION_1_2_FP = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea4; + uint256 constant FRACTION_27_82_FP = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 constant FRACTION_3_82_FP = 0x2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e775; + + // Exponents for inversions and square roots mod P + uint256 constant EXP_INVERSE_FP = 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD45; // P - 2 + uint256 constant EXP_SQRT_FP = 0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52; // (P + 1) / 4; + + // Groth16 alpha point in G1 + uint256 constant ALPHA_X = 7191762284594917313270496592699838670300366763721693826840778095402675094099; + uint256 constant ALPHA_Y = 6689610893311981980012512757431514205505629932471635369619398835507016194527; + + // Groth16 beta point in G2 in powers of i + uint256 constant BETA_NEG_X_0 = 7432452678743287780054266289251424001011104290831611692995936218496470956063; + uint256 constant BETA_NEG_X_1 = 18123316610154995907808360091102759307721691062115296648800005454085039978508; + uint256 constant BETA_NEG_Y_0 = 18138532004479061933338819808622872726696102711363422031504092384344649063398; + uint256 constant BETA_NEG_Y_1 = 2412875426524353066896726063783429656211160668758409430460678375636563964693; + + // Groth16 gamma point in G2 in powers of i + uint256 constant GAMMA_NEG_X_0 = 6893515703761821986376000525040572547627853830347199552209266009657314034231; + uint256 constant GAMMA_NEG_X_1 = 13088418089195761311598988154835674871172676391774493268269386894888204467124; + uint256 constant GAMMA_NEG_Y_0 = 4055497322553729028803820319745081587647721281335855423485384512564338231939; + uint256 constant GAMMA_NEG_Y_1 = 1475219735743682528161090238412236128189870848900950781740983983032426148838; + + // Groth16 delta point in G2 in powers of i + uint256 constant DELTA_NEG_X_0 = 14326929548255454246493212463309102825668430415529859396786379753149606771079; + uint256 constant DELTA_NEG_X_1 = 11689887026907063767739334201310677037034639292694336927611599804586372010862; + uint256 constant DELTA_NEG_Y_0 = 6963136076764944144911980872134653691940342598753462391967541377010513219479; + uint256 constant DELTA_NEG_Y_1 = 16676898437550420220509490368384039920586642507726789429103906256057524588684; + + // Constant and public input points + uint256 constant CONSTANT_X = 2337299022666095870965334581031411636062112822363653072649960945881372178017; + uint256 constant CONSTANT_Y = 13136969239754770688407119288423504407937297704319142769424711501043567132597; + uint256 constant PUB_0_X = 19099476937402324909194208209825562307852182662517509743676497897260504732512; + uint256 constant PUB_0_Y = 20028922012528722057705529708286855480436546445192813910535158033962012218781; + uint256 constant PUB_1_X = 19099476937402324909194208209825562307852182662517509743676497897260504732512; + uint256 constant PUB_1_Y = 20028922012528722057705529708286855480436546445192813910535158033962012218781; + uint256 constant PUB_2_X = 11889244704813280517243848650354300010539043696492425502144908554150056345696; + uint256 constant PUB_2_Y = 15675168965315997779710315205375571650649075586308976753989000299217918262148; + + /// Negation in Fp. + /// @notice Returns a number x such that a + x = 0 in Fp. + /// @notice The input does not need to be reduced. + /// @param a the base + /// @return x the result + function negate(uint256 a) internal pure returns (uint256 x) { + unchecked { + x = (P - (a % P)) % P; // Modulo is cheaper than branching + } + } + + /// Exponentiation in Fp. + /// @notice Returns a number x such that a ^ e = x in Fp. + /// @notice The input does not need to be reduced. + /// @param a the base + /// @param e the exponent + /// @return x the result + function exp(uint256 a, uint256 e) internal view returns (uint256 x) { + bool success; + assembly ("memory-safe") { + let f := mload(0x40) + mstore(f, 0x20) + mstore(add(f, 0x20), 0x20) + mstore(add(f, 0x40), 0x20) + mstore(add(f, 0x60), a) + mstore(add(f, 0x80), e) + mstore(add(f, 0xa0), P) + success := staticcall(gas(), PRECOMPILE_MODEXP, f, 0xc0, f, 0x20) + x := mload(f) + } + if (!success) { + // Exponentiation failed. + // Should not happen. + revert ProofInvalid(); + } + } + + /// Invertsion in Fp. + /// @notice Returns a number x such that a * x = 1 in Fp. + /// @notice The input does not need to be reduced. + /// @notice Reverts with ProofInvalid() if the inverse does not exist + /// @param a the input + /// @return x the solution + function invert_Fp(uint256 a) internal view returns (uint256 x) { + x = exp(a, EXP_INVERSE_FP); + if (mulmod(a, x, P) != 1) { + // Inverse does not exist. + // Can only happen during G2 point decompression. + revert ProofInvalid(); + } + } + + /// Square root in Fp. + /// @notice Returns a number x such that x * x = a in Fp. + /// @notice Will revert with InvalidProof() if the input is not a square + /// or not reduced. + /// @param a the square + /// @return x the solution + function sqrt_Fp(uint256 a) internal view returns (uint256 x) { + x = exp(a, EXP_SQRT_FP); + if (mulmod(x, x, P) != a) { + // Square root does not exist or a is not reduced. + // Happens when G1 point is not on curve. + revert ProofInvalid(); + } + } + + /// Square test in Fp. + /// @notice Returns wheter a number x exists such that x * x = a in Fp. + /// @notice Will revert with InvalidProof() if the input is not a square + /// or not reduced. + /// @param a the square + /// @return x the solution + function isSquare_Fp(uint256 a) internal view returns (bool) { + uint256 x = exp(a, EXP_SQRT_FP); + return mulmod(x, x, P) == a; + } + + /// Square root in Fp2. + /// @notice Fp2 is the complex extension Fp[i]/(i^2 + 1). The input is + /// a0 + a1 ⋅ i and the result is x0 + x1 ⋅ i. + /// @notice Will revert with InvalidProof() if + /// * the input is not a square, + /// * the hint is incorrect, or + /// * the input coefficents are not reduced. + /// @param a0 The real part of the input. + /// @param a1 The imaginary part of the input. + /// @param hint A hint which of two possible signs to pick in the equation. + /// @return x0 The real part of the square root. + /// @return x1 The imaginary part of the square root. + function sqrt_Fp2(uint256 a0, uint256 a1, bool hint) internal view returns (uint256 x0, uint256 x1) { + // If this square root reverts there is no solution in Fp2. + uint256 d = sqrt_Fp(addmod(mulmod(a0, a0, P), mulmod(a1, a1, P), P)); + if (hint) { + d = negate(d); + } + // If this square root reverts there is no solution in Fp2. + x0 = sqrt_Fp(mulmod(addmod(a0, d, P), FRACTION_1_2_FP, P)); + x1 = mulmod(a1, invert_Fp(mulmod(x0, 2, P)), P); + + // Check result to make sure we found a root. + // Note: this also fails if a0 or a1 is not reduced. + if (a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P) + || a1 != mulmod(2, mulmod(x0, x1, P), P)) { + revert ProofInvalid(); + } + } + + /// Compress a G1 point. + /// @notice Reverts with InvalidProof if the coordinates are not reduced + /// or if the point is not on the curve. + /// @notice The point at infinity is encoded as (0,0) and compressed to 0. + /// @param x The X coordinate in Fp. + /// @param y The Y coordinate in Fp. + /// @return c The compresed point (x with one signal bit). + function compress_g1(uint256 x, uint256 y) internal view returns (uint256 c) { + if (x >= P || y >= P) { + // G1 point not in field. + revert ProofInvalid(); + } + if (x == 0 && y == 0) { + // Point at infinity + return 0; + } + + // Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid. + uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); + if (y == y_pos) { + return (x << 1) | 0; + } else if (y == negate(y_pos)) { + return (x << 1) | 1; + } else { + // G1 point not on curve. + revert ProofInvalid(); + } + } + + /// Decompress a G1 point. + /// @notice Reverts with InvalidProof if the input does not represent a valid point. + /// @notice The point at infinity is encoded as (0,0) and compressed to 0. + /// @param c The compresed point (x with one signal bit). + /// @return x The X coordinate in Fp. + /// @return y The Y coordinate in Fp. + function decompress_g1(uint256 c) internal view returns (uint256 x, uint256 y) { + // Note that X = 0 is not on the curve since 0³ + 3 = 3 is not a square. + // so we can use it to represent the point at infinity. + if (c == 0) { + // Point at infinity as encoded in EIP196 and EIP197. + return (0, 0); + } + bool negate_point = c & 1 == 1; + x = c >> 1; + if (x >= P) { + // G1 x coordinate not in field. + revert ProofInvalid(); + } + + // Note: (x³ + 3) is irreducible in Fp, so it can not be zero and therefore + // y can not be zero. + // Note: sqrt_Fp reverts if there is no solution, i.e. the point is not on the curve. + y = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); + if (negate_point) { + y = negate(y); + } + } + + /// Compress a G2 point. + /// @notice Reverts with InvalidProof if the coefficients are not reduced + /// or if the point is not on the curve. + /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). + /// @param x0 The real part of the X coordinate. + /// @param x1 The imaginary poart of the X coordinate. + /// @param y0 The real part of the Y coordinate. + /// @param y1 The imaginary part of the Y coordinate. + /// @return c0 The first half of the compresed point (x0 with two signal bits). + /// @return c1 The second half of the compressed point (x1 unmodified). + function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) + internal view returns (uint256 c0, uint256 c1) { + if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) { + // G2 point not in field. + revert ProofInvalid(); + } + if ((x0 | x1 | y0 | y1) == 0) { + // Point at infinity + return (0, 0); + } + + // Compute y^2 + // Note: shadowing variables and scoping to avoid stack-to-deep. + uint256 y0_pos; + uint256 y1_pos; + { + uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P); + uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); + uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); + y0_pos = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); + y1_pos = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + } + + // Determine hint bit + // If this sqrt fails the x coordinate is not on the curve. + bool hint; + { + uint256 d = sqrt_Fp(addmod(mulmod(y0_pos, y0_pos, P), mulmod(y1_pos, y1_pos, P), P)); + hint = !isSquare_Fp(mulmod(addmod(y0_pos, d, P), FRACTION_1_2_FP, P)); + } + + // Recover y + (y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint); + if (y0 == y0_pos && y1 == y1_pos) { + c0 = (x0 << 2) | (hint ? 2 : 0) | 0; + c1 = x1; + } else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) { + c0 = (x0 << 2) | (hint ? 2 : 0) | 1; + c1 = x1; + } else { + // G1 point not on curve. + revert ProofInvalid(); + } + } + + /// Decompress a G2 point. + /// @notice Reverts with InvalidProof if the input does not represent a valid point. + /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). + /// @param c0 The first half of the compresed point (x0 with two signal bits). + /// @param c1 The second half of the compressed point (x1 unmodified). + /// @return x0 The real part of the X coordinate. + /// @return x1 The imaginary poart of the X coordinate. + /// @return y0 The real part of the Y coordinate. + /// @return y1 The imaginary part of the Y coordinate. + function decompress_g2(uint256 c0, uint256 c1) + internal view returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) { + // Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square. + // so we can use it to represent the point at infinity. + if (c0 == 0 && c1 == 0) { + // Point at infinity as encoded in EIP197. + return (0, 0, 0, 0); + } + bool negate_point = c0 & 1 == 1; + bool hint = c0 & 2 == 2; + x0 = c0 >> 2; + x1 = c1; + if (x0 >= P || x1 >= P) { + // G2 x0 or x1 coefficient not in field. + revert ProofInvalid(); + } + + uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P); + uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); + uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); + + y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); + y1 = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + + // Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve. + // Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero. + // But y0 or y1 may still independently be zero. + (y0, y1) = sqrt_Fp2(y0, y1, hint); + if (negate_point) { + y0 = negate(y0); + y1 = negate(y1); + } + } + + /// Compute the public input linear combination. + /// @notice Reverts with PublicInputNotInField if the input is not in the field. + /// @notice Computes the multi-scalar-multiplication of the public input + /// elements and the verification key including the constant term. + /// @param input The public inputs. These are elements of the scalar field Fr. + /// @return x The X coordinate of the resulting G1 point. + /// @return y The Y coordinate of the resulting G1 point. + function publicInputMSM(uint256[3] calldata input) + internal view returns (uint256 x, uint256 y) { + // Note: The ECMUL precompile does not reject unreduced values, so we check this. + // Note: Unrolling this loop does not cost much extra in code-size, the bulk of the + // code-size is in the PUB_ constants. + // ECMUL has input (x, y, scalar) and output (x', y'). + // ECADD has input (x1, y1, x2, y2) and output (x', y'). + // We call them such that ecmul output is already in the second point + // argument to ECADD so we can have a tight loop. + bool success = true; + assembly ("memory-safe") { + let f := mload(0x40) + let g := add(f, 0x40) + let s + mstore(f, CONSTANT_X) + mstore(add(f, 0x20), CONSTANT_Y) + mstore(g, PUB_0_X) + mstore(add(g, 0x20), PUB_0_Y) + s := calldataload(input) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_1_X) + mstore(add(g, 0x20), PUB_1_Y) + s := calldataload(add(input, 32)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_2_X) + mstore(add(g, 0x20), PUB_2_Y) + s := calldataload(add(input, 64)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + x := mload(f) + y := mload(add(f, 0x20)) + } + if (!success) { + // Either Public input not in field, or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert PublicInputNotInField(); + } + } + + /// Compress a proof. + /// @notice Will revert with InvalidProof if the curve points are invalid, + /// but does not verify the proof itself. + /// @param proof The uncompressed Groth16 proof. Elements are in the same order as for + /// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197. + /// @return compressed The compressed proof. Elements are in the same order as for + /// verifyCompressedProof. I.e. points (A, B, C) in compressed format. + function compressProof(uint256[8] calldata proof) + public view returns (uint256[4] memory compressed) { + compressed[0] = compress_g1(proof[0], proof[1]); + (compressed[2], compressed[1]) = compress_g2(proof[3], proof[2], proof[5], proof[4]); + compressed[3] = compress_g1(proof[6], proof[7]); + } + + /// Verify a Groth16 proof with compressed points. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was succesfully verified. + /// @param compressedProof the points (A, B, C) in compressed format + /// matching the output of compressProof. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyCompressedProof( + uint256[4] calldata compressedProof, + uint256[3] calldata input + ) public view { + (uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]); + (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = decompress_g2( + compressedProof[2], compressedProof[1]); + (uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]); + (uint256 Lx, uint256 Ly) = publicInputMSM(input); + + // Verify the pairing + // Note: The precompile expects the F2 coefficients in big-endian order. + // Note: The pairing precompile rejects unreduced values, so we won't check that here. + uint256[24] memory pairings; + // e(A, B) + pairings[ 0] = Ax; + pairings[ 1] = Ay; + pairings[ 2] = Bx1; + pairings[ 3] = Bx0; + pairings[ 4] = By1; + pairings[ 5] = By0; + // e(C, -δ) + pairings[ 6] = Cx; + pairings[ 7] = Cy; + pairings[ 8] = DELTA_NEG_X_1; + pairings[ 9] = DELTA_NEG_X_0; + pairings[10] = DELTA_NEG_Y_1; + pairings[11] = DELTA_NEG_Y_0; + // e(α, -β) + pairings[12] = ALPHA_X; + pairings[13] = ALPHA_Y; + pairings[14] = BETA_NEG_X_1; + pairings[15] = BETA_NEG_X_0; + pairings[16] = BETA_NEG_Y_1; + pairings[17] = BETA_NEG_Y_0; + // e(L_pub, -γ) + pairings[18] = Lx; + pairings[19] = Ly; + pairings[20] = GAMMA_NEG_X_1; + pairings[21] = GAMMA_NEG_X_0; + pairings[22] = GAMMA_NEG_Y_1; + pairings[23] = GAMMA_NEG_Y_0; + + // Check pairing equation. + bool success; + uint256[1] memory output; + assembly ("memory-safe") { + success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x300, output, 0x20) + } + if (!success || output[0] != 1) { + // Either proof or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert ProofInvalid(); + } + } + + /// Verify an uncompressed Groth16 proof. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was succesfully verified. + /// @param proof the points (A, B, C) in EIP-197 format matching the output + /// of compressProof. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyProof( + uint256[8] calldata proof, + uint256[3] calldata input + ) public view { + (uint256 x, uint256 y) = publicInputMSM(input); + + // Note: The precompile expects the F2 coefficients in big-endian order. + // Note: The pairing precompile rejects unreduced values, so we won't check that here. + + bool success; + assembly ("memory-safe") { + let f := mload(0x40) // Free memory pointer. + + // Copy points (A, B, C) to memory. They are already in correct encoding. + // This is pairing e(A, B) and G1 of e(C, -δ). + calldatacopy(f, proof, 0x100) + + // Complete e(C, -δ) and write e(α, -β), e(L_pub, -γ) to memory. + // OPT: This could be better done using a single codecopy, but + // Solidity (unlike standalone Yul) doesn't provide a way to + // to do this. + mstore(add(f, 0x100), DELTA_NEG_X_1) + mstore(add(f, 0x120), DELTA_NEG_X_0) + mstore(add(f, 0x140), DELTA_NEG_Y_1) + mstore(add(f, 0x160), DELTA_NEG_Y_0) + mstore(add(f, 0x180), ALPHA_X) + mstore(add(f, 0x1a0), ALPHA_Y) + mstore(add(f, 0x1c0), BETA_NEG_X_1) + mstore(add(f, 0x1e0), BETA_NEG_X_0) + mstore(add(f, 0x200), BETA_NEG_Y_1) + mstore(add(f, 0x220), BETA_NEG_Y_0) + mstore(add(f, 0x240), x) + mstore(add(f, 0x260), y) + mstore(add(f, 0x280), GAMMA_NEG_X_1) + mstore(add(f, 0x2a0), GAMMA_NEG_X_0) + mstore(add(f, 0x2c0), GAMMA_NEG_Y_1) + mstore(add(f, 0x2e0), GAMMA_NEG_Y_0) + + // Check pairing equation. + success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x300, f, 0x20) + // Also check returned value (both are either 1 or 0). + success := and(success, mload(f)) + } + if (!success) { + // Either proof or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert ProofInvalid(); + } + } +} diff --git a/contracts/test/verifiers/VerifierPlonk.sol b/contracts/test/verifiers/VerifierPlonk.sol new file mode 100644 index 000000000..97e97e6f1 --- /dev/null +++ b/contracts/test/verifiers/VerifierPlonk.sol @@ -0,0 +1,1021 @@ +// 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.19; + +contract PlonkVerifier { + + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 4983793384399042314514685285588169416118722874476172449762781616967620223650; + uint256 private constant g2_srs_1_x_1 = 6754759659809702461615589994680829062963468502059944990602949359395558725093; + uint256 private constant g2_srs_1_y_0 = 5331505949168118497261346664048967009937600438919278322717103563713059957147; + uint256 private constant g2_srs_1_y_1 = 17918899171254067306883902385521504405796104532858037361358570281963985230108; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 8; + uint256 private constant vk_inv_domain_size = 19152212512859365819465605027100115702479818850364030050735928663253832433665; + uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; + uint256 private constant vk_ql_com_x = 20205283157081902125528657762123841684827379648193571622345244782571495119686; + uint256 private constant vk_ql_com_y = 21781445325313964091213675703446620686011620905054551504505798925570055048063; + uint256 private constant vk_qr_com_x = 490962721355515306782896251905009642168493832672302100311478156385773223808; + uint256 private constant vk_qr_com_y = 5270239512471818815551793567649292792631076552073233008263432301253091160761; + uint256 private constant vk_qm_com_x = 0; + uint256 private constant vk_qm_com_y = 0; + uint256 private constant vk_qo_com_x = 19292694996756178462254602912706368102521676239672741174165871006523154949195; + uint256 private constant vk_qo_com_y = 4653594422812323089240491133537319932723564121642818450335715251388498984312; + uint256 private constant vk_qk_com_x = 0; + uint256 private constant vk_qk_com_y = 0; + + uint256 private constant vk_s1_com_x = 13984411499425252426979037306908471623629545478016463187874526256591009669144; + uint256 private constant vk_s1_com_y = 13329843082682862541917988893852836210961433899024857695150834771710136078858; + + uint256 private constant vk_s2_com_x = 21187293040598246119114144054694675047356712226870389216801786263069433808764; + uint256 private constant vk_s2_com_y = 17590548947506374380575927746142287295771745216104366314033343244050297407117; + + uint256 private constant vk_s3_com_x = 16640448690559526797085628912342799515609712408357461915381186345406083214266; + uint256 private constant vk_s3_com_y = 15440914661538149192522449501002430206648217584625716664820007542182414216623; + + uint256 private constant vk_coset_shift = 5; + + + + + + uint256 private constant vk_nb_commitments_commit_api = 0; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + + + 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_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) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { + error_proof_size() + } + } + + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { + 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} + { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr,0x180), vk_s1_com_x) + mstore(add(mPtr,0x1a0), vk_s1_com_y) + mstore(add(mPtr,0x1c0), vk_s2_com_x) + mstore(add(mPtr,0x1e0), vk_s2_com_y) + + let offset := 0x200 + + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { + 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := add( + aproof, + add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) + mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() + } + res := mload(mPtr) + } + } + } +} diff --git a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol new file mode 100644 index 000000000..084b24ea0 --- /dev/null +++ b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol @@ -0,0 +1,1193 @@ +// 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.19; + +contract PlonkVerifier { + + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 4063571233546661467734799708300131386289831282909879383778035852630874856695; + uint256 private constant g2_srs_1_x_1 = 4647926734714541979239671019576593881493945862077482063229589788724791450632; + uint256 private constant g2_srs_1_y_0 = 15514169863847815805861354127833333882511661051872364096032336682739266504631; + uint256 private constant g2_srs_1_y_1 = 14981245089384445918999453598516659596327553649108599315427712934518517584091; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 64; + uint256 private constant vk_inv_domain_size = 21546239076966786546898805655487630165289796206659533807077919746160561487873; + uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; + uint256 private constant vk_ql_com_x = 8241117109803098541098613984488731009721160645379011473335652343529535266960; + uint256 private constant vk_ql_com_y = 20722965259158873821120253635967463269877724605143204728344162493690267204338; + uint256 private constant vk_qr_com_x = 6655659571137710427173880313823537029253828787683560125550266937179191868532; + uint256 private constant vk_qr_com_y = 13808918852189657493040000991883636660567905880489122692410877765981247442335; + uint256 private constant vk_qm_com_x = 9617414438893381633510036004229347488936383320496724899204335238610971458841; + uint256 private constant vk_qm_com_y = 9139778173198521885297402021941671824119034286476198203166267405240154924689; + uint256 private constant vk_qo_com_x = 6753944061897297345277777124783567586429547954466671056344964801972446730543; + uint256 private constant vk_qo_com_y = 18520936095139799918057373004179121242639392163222661288456393604460483299493; + uint256 private constant vk_qk_com_x = 15533273441448245872993712610675610998153353458964021815595706151938181224464; + uint256 private constant vk_qk_com_y = 7989411376990188833711571569796406054937132408812417153401882189030984814970; + + uint256 private constant vk_s1_com_x = 13250238937038458155524345632477738424994803733052110113126842346865903270013; + uint256 private constant vk_s1_com_y = 15799380157651776438753741634041125968174208046256469113223492112567454413031; + + uint256 private constant vk_s2_com_x = 16290959950757524941113235946220019026974774927006431669767544216586989016814; + uint256 private constant vk_s2_com_y = 12716127461505062001708899285893083752439690218620775280353810691937253438434; + + uint256 private constant vk_s3_com_x = 21721985565414851830858467827731205202151277206551566969624040213120314605650; + uint256 private constant vk_s3_com_y = 5607754375458556197432930148286768768082695251348209961717955451483607620014; + + uint256 private constant vk_coset_shift = 5; + + + uint256 private constant vk_selector_commitments_commit_api_0_x = 11452134746239496272478105026865109049072990374204527231978960641100599564380; + uint256 private constant vk_selector_commitments_commit_api_0_y = 12432732878880068425747106022374326794072801183995506113323301554838024086459; + + + uint256 private constant vk_index_commit_api_0 = 14; + + + uint256 private constant vk_nb_commitments_commit_api = 1; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + + // -------- utils (for hash_fr) + uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant zero_uint256 = 0; + + uint8 private constant lenInBytes = 48; + uint8 private constant sizeDomain = 11; + uint8 private constant one = 1; + uint8 private constant two = 2; + + + 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { + error_proof_size() + } + } + + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + + // mPtr free memory. Computes the public input contribution related to the commit + 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_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) + + + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + 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) + + } + + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr)->res { + + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), 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), lenInBytes) + 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), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { + 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} + { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr,0x180), vk_s1_com_x) + mstore(add(mPtr,0x1a0), vk_s1_com_y) + mstore(add(mPtr,0x1c0), vk_s2_com_x) + mstore(add(mPtr,0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) + mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) + offset := add(offset, 0x40) + + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + + let _mPtr := add(mPtr, add(offset, 0xe0)) + let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { + 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := add( + aproof, + add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) + mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() + } + res := mload(mPtr) + } + } + } +} diff --git a/contracts/test/verifiers/groth16_proof_data.json b/contracts/test/verifiers/groth16_proof_data.json new file mode 100644 index 000000000..25b16d0e1 --- /dev/null +++ b/contracts/test/verifiers/groth16_proof_data.json @@ -0,0 +1,17 @@ +{ + "proof": [ + "0x142faf48b38940f058ecedffabd4e1df52622e658573f49d3e66491c04d70b4a", + "0x1ba0506058eb4d1884e27bddb588ee2678d859b998c497a97747c83d29ae65c2", + "0x0c5cedb8725bef294743da80bcaaf08861acada318ce3517bea732e8b60f6bcb", + "0x1374fefcd8c739678200856b4b35a362b8d813ca61de24006b1a3d70e9adb76e", + "0x0bcfbe942d0abe32dbfc42efeec4d52f405fe9ab244315f5c9f35792677ac39b", + "0x1672df6005d6e018d457ceae873b45af4f9ae7096cccc46b3e736c42388366b3", + "0x123a7fb1a37c9c65c17a7a7acbaa4df2f2554fefa58731959146df8cb4387e94", + "0x21f9d8d258495c61639e6d436413bf39bc0b693334b87d0b745bb41b5638838a" + ], + "inputs": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ] +} \ No newline at end of file diff --git a/contracts/test/verifiers/plonk_proof_data.json b/contracts/test/verifiers/plonk_proof_data.json new file mode 100644 index 000000000..a08a9ca84 --- /dev/null +++ b/contracts/test/verifiers/plonk_proof_data.json @@ -0,0 +1,8 @@ +{ + "proof": "0x15635310727794e740080e2969e5fc69425cbbac5b8f45297df9040a68d1d84830301af25143705dbb92c4b16da98ead08afdf864a582e3bb7b26c49e6802e791c861250ad0d5e24c727b07d1d521d21de0f69534d4829f051ed50e2275af8b02f55b3c6d18d8cf8fbeebb9aed93a23691ed8935741f72d4941ede14c2eab0bc03076aed3f5339027208bd0948717ec26b968823a4cf4a8fa487fe7fe29e13f72dc3a15f7532a782f5b5d9c8511ceb4028003d6c051d3a78bdd0abc93860d46a2e570a7fbed1cfc59bc57464e1b6e9468f0c3e3d30ab5236e6b5d1ff7eee2f4229bc8b1cda05a2a2890b138cabcd0026e10748999e86e90570cb6ae7c2d581701e1440691f96a88dfb6a64ea35346f06bc5342549b388be0c76bf090877258132332300874657f062558e1716e44dd4cd7fbf498d54e54eab8302f9bcea6fd1e07c59cf6ee9729147cc20c993ee90b5c35a85b446e625baac4dd26033bb23298294935d52f60d741dd9349ffd1236d734b190d7ee51d4af7612b0625c1850d001fa390f28b1ee7b9b2635b26b6df9337b2114e032d64128fc051e9de825c5d4228f53084ab96f8d588404f6cac17e5897d5f36ffbf1001e810c8811a074bc62b1f9b5a0bf99ada3a59e58aaf70506d3bf47ca3ab3956ba2a0113d639c6e104f1131b166c09e2780518315517eecb60c9e5402aec675b1db652d981beac86f1890b2483c9c92980e0d3bd45e2757668a72979d5ce62d64e0e9e6bbbfa5ecc64872fdb7a92f70cc2626a5274221525243082a1274bfe83a3caec7bc4a17a9dcbeb25548d0d4945f1c71ba1654fcd63d7481dd85ee0210afe7fd45d6c0fc39d187f009018842e0a6870ee1a8e73614210906ce37c3d9960ecb7630902d175a7eb970fbc83270a2706e2ee9532d7c833d8508bc5507cf4959048b5fbffb58130d4a407a0b5fc9f61cfbf8ef37bfe71e2d0149923bd7eebad69138969aad2de036997019707865d800d8772a9970400e44a094fb604250bb718629218c84d505a68932e612cba931750444fe8da78e824f190619a2869640db4c1fbd3e9c8a6e8b19625402740f9a2d6aeae8736cb69ad95c9048bfa09a94c33ba4d70820759bfb29f22d309a3a479bb09100115ac8087858d339429c2fe475c2eb4d24aa3ac994b8e", + "inputs": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ] +} \ No newline at end of file diff --git a/contracts/test/verifiers/plonk_proof_data_range_check.json b/contracts/test/verifiers/plonk_proof_data_range_check.json new file mode 100644 index 000000000..5d96abc8d --- /dev/null +++ b/contracts/test/verifiers/plonk_proof_data_range_check.json @@ -0,0 +1,8 @@ +{ + "proof": "0x1a209cbdaee542d883cb031d1d115262a938853130254d0bb98e29d45eeb505227b1187953d73bcbd43128c232b99840a06fc24c30d0b62f2c4ade45400b34be0fb094ae2cba56a816146c7c83e8d9966c7f0ff5c8df7054e80d0a5d20dc4b42059d0d37ad3eb9e9250b98d3df0dfd4588097c3d70172f12c6f745d5fd52066a113468580dabb552b5de7ba351db863a20e1832521d1165c115cc56a1dd320211fff83295b457af36e51d2bddc13f43a134f84a52104594409a433cedabab57910b6b102521889d140b8121c7d4f2eeb1b260f58e772c9606d77a71fcba09b69285148cfd1d120420c78d0fad4451bb5bef6d767dc445bac8e3d5c14a619714412444dab59cb2807b0bbb6fb09a7af9c0184d1dcf39fdf8dc21ed389f128f8462a89cecb7f70ecd0ea0bc2109a0d2cca692ad73d9a609d57f3c9eaa9e4c33e8d07fccfa5e1f4a3be2f132b09b20f2867254dd42e2d8584ccbaa197a2369cca371e616747bc5586f58210007d6787e673e73eff25b741c3c49ded1f5360e77cb81382bcbfe306b42198fa42620447bb9cad3f552883cbd40e9f32b3e0d994b7601646efe00613c5dd7342f14f7cde3826be36de663fedc2be3b78c4f859d476b12ba5661f4c0419b9e1bab5a419fc1f8f21281920ef1b40cf63d849e2d89f79fd294898f8c44efa73fdef74e3cdbcc1f6374dd8f539e16d8d7468058aa8fea8ca146db1b01353a2a3871a0b2736ccc60a6cb201a3199dbd48acd6d95e8e6f99222120bdcc4f1c35a0181247219404274ef333f99c01c843e4981dfc8dfc6e57c62d45adf96d7cb6912a6b68ef26400d58604852f7046253dd980c8711f14a9e2d07df5efc45bd9079f78bb971d25291793db219105d3cff1deba34343fc653e230796603cc93efd7bee1d043d45cf42f7e6e280b976f71190156624668c4c591900f7cf3f08a6dc670740133382894d87ee11efca08fcc5ea8aba2f213b1f9d9e0ce4e523a6a0029943b141560b35fd892103d53d3d221828eecc60b60fb08e0f1e3872cb287096124e5e58bff91935a577d8cacf4f827fce2ad3c055aefc092200abbefec71ac1c99cd6f431b9baa3111523947a4013fa1e215573f704c5094a058adfe9298c02a4b7cf2e48177a8d0537af7d5166fc7e27b1ba408468615eb61b1436373b86bacfa8cbbe8040368f390ac84c9c9ed9e23b183bf458134e75022f2f21cf2a6d83f51f2b32adee91e36a0a678d0bfd12ddf141fe3115be40422600b0f02b50ad27b72856d06e6124c75e25beb6c679db800d22d95e3e0fbe7fde", + "inputs": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ] +} \ No newline at end of file diff --git a/plonky2x/verifier/gnark_verifiers_test.go b/plonky2x/verifier/gnark_verifiers_test.go new file mode 100644 index 000000000..d1698c1d3 --- /dev/null +++ b/plonky2x/verifier/gnark_verifiers_test.go @@ -0,0 +1,228 @@ +// Useful reference files in gnark: +// https://github.com/Consensys/gnark-solidity-checker/blob/main/cmd/templates.go +// https://github.com/Consensys/gnark/blob/cfe83dbce12428ad0b095bcc33de55c6a9121949/test/assert_solidity.go#L60-L77 +package main + +import ( + "bufio" + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" + plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" + "github.com/consensys/gnark/std/rangecheck" + "github.com/consensys/gnark/test" +) + +type MyCircuit struct { + X frontend.Variable `gnark:",public"` + Y frontend.Variable `gnark:",public"` + Z frontend.Variable `gnark:",public"` + DoRangeCheck bool +} + +func (circuit *MyCircuit) Define(api frontend.API) error { + api.AssertIsEqual(circuit.Z, api.Add(circuit.X, circuit.Y)) + if circuit.DoRangeCheck { + rangeChecker := rangecheck.New(api) + rangeChecker.Check(circuit.X, 8) + } + return nil +} + +type Groth16ProofData struct { + Proof []string `json:"proof"` + Inputs []string `json:"inputs"` +} + +func TestGroth16(t *testing.T) { + + circuit := MyCircuit{DoRangeCheck: false} + + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + panic(err) + } + pk, vk, err := groth16.Setup(r1cs) + if err != nil { + panic(err) + } + + buf := new(bytes.Buffer) + err = vk.ExportSolidity(buf) + if err != nil { + panic(err) + } + content := buf.String() + + contractFile, err := os.Create("VerifierGroth16.sol") + if err != nil { + panic(err) + } + w := bufio.NewWriter(contractFile) + // write the new content to the writer + _, err = w.Write([]byte(content)) + if err != nil { + panic(err) + } + contractFile.Close() + + assignment := MyCircuit{ + X: 1, + Y: 2, + Z: 3, + } + + witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) + proof, _ := groth16.Prove(r1cs, pk, witness) + + const fpSize = 4 * 8 + buf = new(bytes.Buffer) + proof.WriteRawTo(buf) + proofBytes := buf.Bytes() + + proofs := make([]string, 8) + // Print out the proof + for i := 0; i < 8; i++ { + proofs[i] = "0x" + hex.EncodeToString(proofBytes[i*fpSize:(i+1)*fpSize]) + } + + publicWitness, _ := witness.Public() + publicWitnessBytes, _ := publicWitness.MarshalBinary() + publicWitnessBytes = publicWitnessBytes[12:] // We cut off the first 12 bytes because they encode length information + + inputs := make([]string, 3) + // Print out the public witness bytes + for i := 0; i < 3; i++ { + inputs[i] = "0x" + hex.EncodeToString(publicWitnessBytes[i*fpSize:(i+1)*fpSize]) + } + + // Create the data struct and populate it + data := Groth16ProofData{ + Proof: proofs, + Inputs: inputs, + } + + // Marshal the data into JSON + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + fmt.Println("Error marshalling to JSON:", err) + return + } + + // Write the JSON to a file + err = ioutil.WriteFile("groth16_proof_data.json", jsonData, 0644) + if err != nil { + fmt.Println("Error writing to file:", err) + } +} + +type PlonkProofData struct { + Proof string `json:"proof"` + Inputs []string `json:"inputs"` +} + +func TestPlonk(t *testing.T) { + + range_check := false + + circuit := MyCircuit{DoRangeCheck: range_check} + + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) + if err != nil { + panic(err) + } + srs, err := test.NewKZGSRS(r1cs) + if err != nil { + panic(err) + } + pk, vk, err := plonk.Setup(r1cs, srs) + if err != nil { + panic(err) + } + + buf := new(bytes.Buffer) + err = vk.ExportSolidity(buf) + if err != nil { + panic(err) + } + content := buf.String() + + filename := "VerifierPlonk.sol" + if range_check { + filename = "VerifierPlonkRangeCheck.sol" + } + contractFile, err := os.Create(filename) + if err != nil { + panic(err) + } + w := bufio.NewWriter(contractFile) + // write the new content to the writer + _, err = w.Write([]byte(content)) + if err != nil { + panic(err) + } + contractFile.Close() + + assignment := MyCircuit{ + X: 1, + Y: 2, + Z: 3, + } + + witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) + proof, _ := plonk.Prove(r1cs, pk, witness) + + const fpSize = 4 * 8 + + // Taken from: https://github.com/Consensys/gnark/blob/cfe83dbce12428ad0b095bcc33de55c6a9121949/test/assert_solidity.go#L72-L74 + _proof := proof.(*plonk_bn254.Proof) + proofStr := "0x" + hex.EncodeToString(_proof.MarshalSolidity()) + + // Taken from: + // https://github.com/Consensys/gnark/blob/cfe83dbce12428ad0b095bcc33de55c6a9121949/test/assert_solidity.go#L80-L87 + publicWitness, _ := witness.Public() + publicWitnessBytes, _ := publicWitness.MarshalBinary() + publicWitnessBytes = publicWitnessBytes[12:] // We cut off the first 12 bytes because they encode length information + + nbInputs := len(publicWitnessBytes) / fpSize + + inputs := make([]string, nbInputs) + for i := 0; i < nbInputs; i++ { + inputs[i] = "0x" + hex.EncodeToString(publicWitnessBytes[i*fpSize:(i+1)*fpSize]) + } + + // Create the data struct and populate it + data := PlonkProofData{ + Proof: proofStr, + Inputs: inputs, + } + + // Marshal the data into JSON + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + fmt.Println("Error marshalling to JSON:", err) + return + } + + filename = "plonk_proof_data.json" + if range_check { + filename = "plonk_proof_data_range_check.json" + } + + // Write the JSON to a file + err = ioutil.WriteFile(filename, jsonData, 0644) + if err != nil { + fmt.Println("Error writing to file:", err) + } +} From 53bded2def775ea3bdc94663bbd52f9812433730 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Fri, 13 Oct 2023 01:15:48 -0700 Subject: [PATCH 16/30] Updated logic in backend/function and removed assets/Verifier.sol --- assets/Verifier.sol | 1193 -------------------- assets/Verifier_old.sol | 307 ----- plonky2x/core/src/backend/function/args.rs | 6 + plonky2x/core/src/backend/function/mod.rs | 13 +- 4 files changed, 13 insertions(+), 1506 deletions(-) delete mode 100644 assets/Verifier.sol delete mode 100644 assets/Verifier_old.sol diff --git a/assets/Verifier.sol b/assets/Verifier.sol deleted file mode 100644 index cb36ae081..000000000 --- a/assets/Verifier.sol +++ /dev/null @@ -1,1193 +0,0 @@ -// 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.19; - -contract PlonkVerifier { - - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 8092372495400684101491705897324646255244873767278146792539818137343983415338; - uint256 private constant g2_srs_1_x_1 = 17267001818546224117620126850851175661030766706904745275133569110744678569705; - uint256 private constant g2_srs_1_y_0 = 18322000439618828872938506585690152355173339417118410557175261086259665139770; - uint256 private constant g2_srs_1_y_1 = 18361807460194502705595998955236116550325284436398091999656305930880342338897; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 33554432; - uint256 private constant vk_inv_domain_size = 21888242219518804655518433051623070663413851959604507555939307129453691614729; - uint256 private constant vk_omega = 19200870435978225707111062059747084165650991997241425080699860725083300967194; - uint256 private constant vk_ql_com_x = 10475058859656454050695647591133172935508386459718059756898410999806106551072; - uint256 private constant vk_ql_com_y = 6213293053341394200050131794243928751814286941843296559783203899744980812514; - uint256 private constant vk_qr_com_x = 7945139303378761555058466625722713607707726441206159286869501528418026434149; - uint256 private constant vk_qr_com_y = 156590339282025629533899133000896162239385491129921159716602619014566566552; - uint256 private constant vk_qm_com_x = 13992154963319852095604792769360675745515593362505622350879424943943884278192; - uint256 private constant vk_qm_com_y = 20766012900377030563947776646862657527189870601611259870484355975445050379609; - uint256 private constant vk_qo_com_x = 8470301768743093139793165421641707313403445331488881272110105479663725995698; - uint256 private constant vk_qo_com_y = 21592895551100565151417052280131103082580614486310904268527641906481602947; - uint256 private constant vk_qk_com_x = 5654499058770597633228728857640171762263824376475793805538552412908288004447; - uint256 private constant vk_qk_com_y = 6419423782223253913146410978605915715045763868982098455567651000305029892860; - - uint256 private constant vk_s1_com_x = 3784233983189669014310841948904857803160351135759376541936942216013730316026; - uint256 private constant vk_s1_com_y = 4129762303298768564961482720784423245788144573686711108417866217535238720827; - - uint256 private constant vk_s2_com_x = 14325725790615933789083728643797722641545097765592717304379756769414822396384; - uint256 private constant vk_s2_com_y = 9206760818634532501699551379878233042314021497306309900329293412337859918890; - - uint256 private constant vk_s3_com_x = 1889439673607470886202316687133798310780298929819093754909820878439514588802; - uint256 private constant vk_s3_com_y = 19385229208858775427536138610836306884212006605102248507339743025747156971303; - - uint256 private constant vk_coset_shift = 5; - - - uint256 private constant vk_selector_commitments_commit_api_0_x = 4117073030668173863289093683350495260595200061919445680132579228612965095242; - uint256 private constant vk_selector_commitments_commit_api_0_y = 11919248650124787763761338505073673225954611652034365598154808475925452350203; - - - uint256 private constant vk_index_commit_api_0 = 12269855; - - - uint256 private constant vk_nb_commitments_commit_api = 1; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - - // -------- utils (for hash_fr) - uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 - uint256 private constant zero_uint256 = 0; - - uint8 private constant lenInBytes = 48; - uint8 private constant sizeDomain = 11; - uint8 private constant one = 1; - uint8 private constant two = 2; - - - 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) - l_pi := addmod(l_wocommit, l_pi, r_mod) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { - error_proof_size() - } - } - - function check_proof_openings_size(aproof) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced)->beta_not_reduced{ - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - - // mPtr free memory. Computes the public input contribution related to the commit - 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_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) - - - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - 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) - - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr)->res { - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), 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), lenInBytes) - 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), sizeDomain) - - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { - 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} - { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr,0x180), vk_s1_com_x) - mstore(add(mPtr,0x1a0), vk_s1_com_y) - mstore(add(mPtr,0x1c0), vk_s2_com_x) - mstore(add(mPtr,0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) - mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) - offset := add(offset, 0x40) - - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - - let _mPtr := add(mPtr, add(offset, 0xe0)) - let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { - 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := add( - aproof, - add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) - mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() - } - res := mload(mPtr) - } - } - } -} diff --git a/assets/Verifier_old.sol b/assets/Verifier_old.sol deleted file mode 100644 index 78b271bbb..000000000 --- a/assets/Verifier_old.sol +++ /dev/null @@ -1,307 +0,0 @@ - -// SPDX-License-Identifier: AML -// -// Copyright 2017 Christian Reitwiessner -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. - -// 2019 OKIMS - -pragma solidity ^0.8.16; - -library Pairing { - - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct G1Point { - uint256 X; - uint256 Y; - } - - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } - - /* - * @return The negation of p, i.e. p.plus(p.negate()) should be zero. - */ - function negate(G1Point memory p) internal pure returns (G1Point memory) { - - // The prime q in the base field F_q for G1 - if (p.X == 0 && p.Y == 0) { - return G1Point(0, 0); - } else { - return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); - } - } - - /* - * @return The sum of two points of G1 - */ - function plus( - G1Point memory p1, - G1Point memory p2 - ) internal view returns (G1Point memory r) { - - uint256[4] memory input; - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = p2.Y; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - - require(success,"pairing-add-failed"); - } - - - /* - * Same as plus but accepts raw input instead of struct - * @return The sum of two points of G1, one is represented as array - */ - function plus_raw(uint256[4] memory input, G1Point memory r) internal view { - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 {invalid()} - } - - require(success, "pairing-add-failed"); - } - - /* - * @return The product of a point on G1 and a scalar, i.e. - * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all - * points p. - */ - function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { - - uint256[3] memory input; - input[0] = p.X; - input[1] = p.Y; - input[2] = s; - bool success; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - require (success,"pairing-mul-failed"); - } - - - /* - * Same as scalar_mul but accepts raw input instead of struct, - * Which avoid extra allocation. provided input can be allocated outside and re-used multiple times - */ - function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view { - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 {invalid()} - } - require(success, "pairing-mul-failed"); - } - - /* @return The result of computing the pairing check - * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 - * For example, - * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. - */ - function pairing( - G1Point memory a1, - G2Point memory a2, - G1Point memory b1, - G2Point memory b2, - G1Point memory c1, - G2Point memory c2, - G1Point memory d1, - G2Point memory d2 - ) internal view returns (bool) { - - G1Point[4] memory p1 = [a1, b1, c1, d1]; - G2Point[4] memory p2 = [a2, b2, c2, d2]; - uint256 inputSize = 24; - uint256[] memory input = new uint256[](inputSize); - - for (uint256 i = 0; i < 4; i++) { - uint256 j = i * 6; - input[j + 0] = p1[i].X; - input[j + 1] = p1[i].Y; - input[j + 2] = p2[i].X[0]; - input[j + 3] = p2[i].X[1]; - input[j + 4] = p2[i].Y[0]; - input[j + 5] = p2[i].Y[1]; - } - - uint256[1] memory out; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - - require(success,"pairing-opcode-failed"); - - return out[0] != 0; - } -} - -contract Verifier { - - using Pairing for *; - - uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct VerifyingKey { - Pairing.G1Point alfa1; - Pairing.G2Point beta2; - Pairing.G2Point gamma2; - Pairing.G2Point delta2; - // []G1Point IC (K in gnark) appears directly in verifyProof - } - - struct Proof { - Pairing.G1Point A; - Pairing.G2Point B; - Pairing.G1Point C; - } - - function verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.alfa1 = Pairing.G1Point(uint256(11020682126511168068160839679513889820813495861687678032140546067505531899092), uint256(5995328408262325819084541866941201935953243570305245558736929313802212001310)); - vk.beta2 = Pairing.G2Point([uint256(21692440125578184783857570388757267057140185352681860514052043427665453364801), uint256(15659896727025329910957273531332288720365326121006411758011965480663422924083)], [uint256(19028298600807183596556930202724519452796103375122979600632814652622994276436), uint256(19135704786862811638037330823020542674409825032148237442904765642474718994099)]); - vk.gamma2 = Pairing.G2Point([uint256(5366804362744839936515733233163925293622762608834071595895261894577612524603), uint256(13377946564735682308447861567067212951909700069657596665687636357849445285773)], [uint256(545228656900463501759782221345154972903251490355894212998066931928509461970), uint256(4475313117223647925880018085413127878343273821258184606834330199586258187690)]); - vk.delta2 = Pairing.G2Point([uint256(7040107673820945049142254998362889096779897272424860581697388639354731895760), uint256(275018460132017694013804586160820246090854203238980097454702642709574181670)], [uint256(17660627214538717600258808245166026882125201018137164375663395192013746255172), uint256(2378381866927591964998863532641261971962047219870673411534433987909560472428)]); - } - - - // accumulate scalarMul(mul_input) into q - // that is computes sets q = (mul_input[0:2] * mul_input[3]) + q - function accumulate( - uint256[3] memory mul_input, - Pairing.G1Point memory p, - uint256[4] memory buffer, - Pairing.G1Point memory q - ) internal view { - // computes p = mul_input[0:2] * mul_input[3] - Pairing.scalar_mul_raw(mul_input, p); - - // point addition inputs - buffer[0] = q.X; - buffer[1] = q.Y; - buffer[2] = p.X; - buffer[3] = p.Y; - - // q = p + q - Pairing.plus_raw(buffer, q); - } - - /* - * @returns Whether the proof is valid given the hardcoded verifying key - * above and the public inputs - */ - function verifyProof( - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - uint256[3] calldata input - ) public view returns (bool r) { - - Proof memory proof; - proof.A = Pairing.G1Point(a[0], a[1]); - proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); - proof.C = Pairing.G1Point(c[0], c[1]); - - // Make sure that proof.A, B, and C are each less than the prime q - require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); - require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); - - require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); - require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); - - require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); - require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); - - require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); - require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); - - // Make sure that every input is less than the snark scalar field - for (uint256 i = 0; i < input.length; i++) { - require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); - } - - VerifyingKey memory vk = verifyingKey(); - - // Compute the linear combination vk_x - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - - // Buffer reused for addition p1 + p2 to avoid memory allocations - // [0:2] -> p1.X, p1.Y ; [2:4] -> p2.X, p2.Y - uint256[4] memory add_input; - - // Buffer reused for multiplication p1 * s - // [0:2] -> p1.X, p1.Y ; [3] -> s - uint256[3] memory mul_input; - - // temporary point to avoid extra allocations in accumulate - Pairing.G1Point memory q = Pairing.G1Point(0, 0); - - vk_x.X = uint256(6166875854139708612521431984106466585837217993205781805493439401303829446279); // vk.K[0].X - vk_x.Y = uint256(5176386881937885470118103638958661997739679753062074457256694118824627869314); // vk.K[0].Y - mul_input[0] = uint256(0); // vk.K[1].X - mul_input[1] = uint256(0); // vk.K[1].Y - mul_input[2] = input[0]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[1] * input[0] - mul_input[0] = uint256(0); // vk.K[2].X - mul_input[1] = uint256(0); // vk.K[2].Y - mul_input[2] = input[1]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[2] * input[1] - mul_input[0] = uint256(0); // vk.K[3].X - mul_input[1] = uint256(0); // vk.K[3].Y - mul_input[2] = input[2]; - accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[3] * input[2] - - return Pairing.pairing( - Pairing.negate(proof.A), - proof.B, - vk.alfa1, - vk.beta2, - vk_x, - vk.gamma2, - proof.C, - vk.delta2 - ); - } -} diff --git a/plonky2x/core/src/backend/function/args.rs b/plonky2x/core/src/backend/function/args.rs index 46c9aff3c..a26515d9c 100644 --- a/plonky2x/core/src/backend/function/args.rs +++ b/plonky2x/core/src/backend/function/args.rs @@ -5,6 +5,12 @@ use clap::{Parser, Subcommand}; pub struct BuildArgs { #[arg(long, default_value = "./build")] pub build_dir: String, + + // This argument is needed to generate the FunctionVerifier.sol contract that contains + // the verification smart contract for the gnark-plonky-2 verifier. + // This needs to match the argument in `ProveArgs` + #[arg(long, default_value = "/verifier-build")] + pub wrapper_path: String, } #[derive(Parser, Debug, Clone)] diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 6af929f63..2d9cf2f36 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -20,8 +20,6 @@ use crate::backend::wrapper::wrap::WrappedCircuit; use crate::frontend::builder::CircuitIO; use crate::prelude::{CircuitBuilder, GateRegistry, HintRegistry}; -const VERIFIER_CONTRACT: &str = include_str!("../../../../../assets/Verifier.sol"); - /// `Plonky2xFunction`s have all necessary code for a circuit to be deployed end-to-end. pub trait Plonky2xFunction { /// Builds the circuit and saves it to disk. @@ -51,7 +49,7 @@ pub trait Plonky2xFunction { fn entrypoint(); /// Returns the verifier contract for the circuit. - fn verifier(circuit_digest: &str) -> String; + fn verifier(circuit_digest: &str, wrapper_path: &String) -> String; } impl Plonky2xFunction for C { @@ -117,7 +115,7 @@ impl Plonky2xFunction for C { padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_bytes); let circuit_digest = format!("0x{}", hex::encode(padded)); - let verifier_contract = Self::verifier(&circuit_digest); + let verifier_contract = Self::verifier(&circuit_digest, &args.wrapper_path); contract_file .write_all(verifier_contract.as_bytes()) .unwrap(); @@ -243,8 +241,11 @@ impl Plonky2xFunction for C { } } - fn verifier(circuit_digest: &str) -> String { - let generated_contract = VERIFIER_CONTRACT + fn verifier(circuit_digest: &str, wrapper_path: &String) -> String { + let wrapper_verifier_path = format!("{}/Verifier.sol", wrapper_path); + let wrapper_verifier_contract = fs::read_to_string(wrapper_verifier_path) + .expect("Failed to read wrapper_verifier_path"); + let generated_contract = wrapper_verifier_contract .replace("pragma solidity ^0.8.19;", "pragma solidity ^0.8.16;") .replace("function Verify", "function verifyProof"); From 9c1a69e9f68ffee5f9af0df918564ff575c85b5f Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Fri, 13 Oct 2023 09:44:06 -0700 Subject: [PATCH 17/30] nits --- plonky2x/core/src/backend/function/mod.rs | 10 ++++---- plonky2x/core/src/backend/wrapper/wrap.rs | 4 +-- plonky2x/core/src/frontend/vars/bytes32.rs | 2 +- plonky2x/verifier/prover.go | 29 ++++++++++++++++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 2d9cf2f36..b52c39634 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -89,7 +89,7 @@ impl Plonky2xFunction for C { info!("First building wrapper circuit to get the wrapper circuit digest..."); // The wrapper circuit digest will get saved in the Solidity smart contract, - // which will use this value as a public input `VerifierDigest` in the Gnark plonky2 verifier + // which will use this value as a public input `VerifierDigest` in the gnark plonky2 verifier let wrapped_circuit = WrappedCircuit::::build(circuit); let mut circuit_digest_bytes = wrapped_circuit @@ -99,14 +99,14 @@ impl Plonky2xFunction for C { .circuit_digest .to_bytes(); // to_bytes() returns the representation as LE, but we want to save it on-chain - // as BE because that is the format of the public input to the Gnark plonky2 verifier. + // as BE because that is the format of the public input to the gnark plonky2 verifier. circuit_digest_bytes.reverse(); // The VerifierDigest is stored on-chain as a bytes32, so we need to pad it with 0s // to store it in the Solidity smart contract. // Note that we don't need to do any sort of truncation of the top bits because the // circuit digest already lives in the bn254 field because the WrappedCircuit config - // is the Poseidon Bn254 hasher. + // is the Poseidon bn254 hasher. // In fact in the Solidity smart contract we should *not* truncate the top 3 bits // like we do with input_hash and output_hash, as the circuit digest has a // small probability of being greater than 2^253, as the field modulus is 254 bits. @@ -173,8 +173,8 @@ impl Plonky2xFunction for C { ); if let PublicOutput::Bytes(output_bytes) = output { - // TODO: can optimize this by saving the wrapped circuit in build and loading it from a binary - // instead of rebuilding every time. + // It's quite fast (~5-10 seconds) to rebuild the wrapped circuit. Because of this we + // choose to rebuild here instead of loading from disk. let wrapped_circuit = WrappedCircuit::::build(circuit); let wrapped_proof = wrapped_circuit.prove(&proof).expect("failed to wrap proof"); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index baa62f53a..209eb50f6 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -74,8 +74,8 @@ where // We must truncate the top 3 bits because in the gnark-plonky2-verifier, the input_hash // and output_hash are both represented as 1 field element in the BN254 field // to reduce on-chain verification costs. - let input_hash_zeroed = hash_builder.zero_top_bits(input_hash, 3); - let output_hash_zeroed = hash_builder.zero_top_bits(output_hash, 3); + let input_hash_zeroed = hash_builder.mask_be_bits(input_hash, 3); + let output_hash_zeroed = hash_builder.mask_be_bits(output_hash, 3); let input_vars = input_hash_zeroed .as_bytes() diff --git a/plonky2x/core/src/frontend/vars/bytes32.rs b/plonky2x/core/src/frontend/vars/bytes32.rs index d43e00be2..9cb100e0e 100644 --- a/plonky2x/core/src/frontend/vars/bytes32.rs +++ b/plonky2x/core/src/frontend/vars/bytes32.rs @@ -112,7 +112,7 @@ impl SSZVariable for Bytes32Variable { impl, const D: usize> CircuitBuilder { // Returns a Bytes32Variable with the first `num_bits` set to 0. - pub fn zero_top_bits(&mut self, original: Bytes32Variable, num_bits: usize) -> Bytes32Variable { + pub fn mask_be_bits(&mut self, original: Bytes32Variable, num_bits: usize) -> Bytes32Variable { let variables = original.variables(); let mut new_variables = vec![]; for _ in 0..num_bits { diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go index 18a9e6767..1f8edbc03 100644 --- a/plonky2x/verifier/prover.go +++ b/plonky2x/verifier/prover.go @@ -11,6 +11,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/plonk" plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" @@ -136,6 +137,34 @@ func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk plonk.Provin proofFile.Close() log.Info().Msg("Successfully saved proof") + // Write proof with all the public inputs and save to disk. + jsonProofWithWitness, err := json.Marshal(struct { + InputHash hexutil.Bytes `json:"input_hash"` + OutputHash hexutil.Bytes `json:"output_hash"` + VerifierDigest hexutil.Bytes `json:"verifier_digest"` + Proof hexutil.Bytes `json:"proof"` + }{ + InputHash: inputHash.Bytes(), + OutputHash: outputHash.Bytes(), + VerifierDigest: (verifierOnlyCircuitData.CircuitDigest).(*big.Int).Bytes(), + Proof: _proof.MarshalSolidity(), + }) + if err != nil { + return nil, nil, fmt.Errorf("failed to marshal proof with witness: %w", err) + } + proofFile, err = os.Create("proof_with_witness.json") + if err != nil { + return nil, nil, fmt.Errorf("failed to create proof_with_witness file: %w", err) + } + _, err = proofFile.Write(jsonProofWithWitness) + if err != nil { + return nil, nil, fmt.Errorf("failed to write proof_with_witness file: %w", err) + } + proofFile.Close() + log.Info().Msg("Proof with witness") + log.Info().Msg(string(jsonProofWithWitness)) + log.Info().Msg("Successfully saved proof_with_witness") + publicWitness, err := witness.Public() if err != nil { return nil, nil, fmt.Errorf("failed to get public witness: %w", err) From f88dfb2ab9adde3cbc0116623d5a30d298c2181e Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Fri, 13 Oct 2023 09:48:54 -0700 Subject: [PATCH 18/30] Forge fmt --- contracts/test/verifiers/TestVerifier.t.sol | 21 +- contracts/test/verifiers/VerifierGroth16.sol | 82 +- contracts/test/verifiers/VerifierPlonk.sol | 1960 +++++++------- .../verifiers/VerifierPlonkRangeCheck.sol | 2293 ++++++++--------- 4 files changed, 2130 insertions(+), 2226 deletions(-) diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol index 6a702e48f..4db0f5f66 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -15,9 +15,7 @@ contract VerifierTest is Test { function testVerifierGroth16() public { Groth16Verifier verifier = new Groth16Verifier(); - string memory groth16Json = vm.readFile( - "test/verifiers/groth16_proof_data.json" - ); + string memory groth16Json = vm.readFile("test/verifiers/groth16_proof_data.json"); uint256[] memory proof = stdJson.readUintArray(groth16Json, "$.proof"); uint256[] memory input = stdJson.readUintArray(groth16Json, "$.inputs"); @@ -35,23 +33,16 @@ contract VerifierTest is Test { uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); - uint256[4] memory compressedProof = verifier.compressProof( - proofConverted - ); + uint256[4] memory compressedProof = verifier.compressProof(proofConverted); startGas = gasleft(); verifier.verifyCompressedProof(compressedProof, inputConverted); endGas = gasleft(); - console.log( - "gas used for verifying compressed proof: %d", - startGas - endGas - ); + console.log("gas used for verifying compressed proof: %d", startGas - endGas); } function testVerifierPlonk() public { PlonkVerifier verifier = new PlonkVerifier(); - string memory proofJson = vm.readFile( - "test/verifiers/plonk_proof_data.json" - ); + string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); @@ -62,9 +53,7 @@ contract VerifierTest is Test { function testVerifierPlonkRangeCheck() public { PlonkRangeCheckVerifier verifier = new PlonkRangeCheckVerifier(); - string memory proofJson = vm.readFile( - "test/verifiers/plonk_proof_data_range_check.json" - ); + string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data_range_check.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); diff --git a/contracts/test/verifiers/VerifierGroth16.sol b/contracts/test/verifiers/VerifierGroth16.sol index 6cac2b170..090737fa9 100644 --- a/contracts/test/verifiers/VerifierGroth16.sol +++ b/contracts/test/verifiers/VerifierGroth16.sol @@ -1,4 +1,3 @@ - // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -10,7 +9,6 @@ pragma solidity ^0.8.0; /// to compress proofs. /// @notice See for further explanation. contract Verifier { - /// Some of the provided public input values are larger than the field modulus. /// @dev Public input elements are not automatically reduced, as this is can be /// a dangerous source of bugs. @@ -118,7 +116,7 @@ contract Verifier { // Exponentiation failed. // Should not happen. revert ProofInvalid(); - } + } } /// Invertsion in Fp. @@ -186,8 +184,7 @@ contract Verifier { // Check result to make sure we found a root. // Note: this also fails if a0 or a1 is not reduced. - if (a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P) - || a1 != mulmod(2, mulmod(x0, x1, P), P)) { + if (a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P) || a1 != mulmod(2, mulmod(x0, x1, P), P)) { revert ProofInvalid(); } } @@ -208,7 +205,7 @@ contract Verifier { // Point at infinity return 0; } - + // Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid. uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); if (y == y_pos) { @@ -254,7 +251,7 @@ contract Verifier { /// @notice Reverts with InvalidProof if the coefficients are not reduced /// or if the point is not on the curve. /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) - /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). /// @param x0 The real part of the X coordinate. /// @param x1 The imaginary poart of the X coordinate. @@ -263,7 +260,10 @@ contract Verifier { /// @return c0 The first half of the compresed point (x0 with two signal bits). /// @return c1 The second half of the compressed point (x1 unmodified). function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) - internal view returns (uint256 c0, uint256 c1) { + internal + view + returns (uint256 c0, uint256 c1) + { if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) { // G2 point not in field. revert ProofInvalid(); @@ -278,11 +278,11 @@ contract Verifier { uint256 y0_pos; uint256 y1_pos; { - uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P); + uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); y0_pos = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); - y1_pos = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + y1_pos = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); } // Determine hint bit @@ -296,10 +296,10 @@ contract Verifier { // Recover y (y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint); if (y0 == y0_pos && y1 == y1_pos) { - c0 = (x0 << 2) | (hint ? 2 : 0) | 0; + c0 = (x0 << 2) | (hint ? 2 : 0) | 0; c1 = x1; } else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) { - c0 = (x0 << 2) | (hint ? 2 : 0) | 1; + c0 = (x0 << 2) | (hint ? 2 : 0) | 1; c1 = x1; } else { // G1 point not on curve. @@ -310,7 +310,7 @@ contract Verifier { /// Decompress a G2 point. /// @notice Reverts with InvalidProof if the input does not represent a valid point. /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) - /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). /// @param c0 The first half of the compresed point (x0 with two signal bits). /// @param c1 The second half of the compressed point (x1 unmodified). @@ -319,7 +319,10 @@ contract Verifier { /// @return y0 The real part of the Y coordinate. /// @return y1 The imaginary part of the Y coordinate. function decompress_g2(uint256 c0, uint256 c1) - internal view returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) { + internal + view + returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) + { // Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square. // so we can use it to represent the point at infinity. if (c0 == 0 && c1 == 0) { @@ -335,12 +338,12 @@ contract Verifier { revert ProofInvalid(); } - uint256 n3ab = mulmod(mulmod(x0, x1, P), P-3, P); + uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); - y1 = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + y1 = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); // Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve. // Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero. @@ -359,8 +362,7 @@ contract Verifier { /// @param input The public inputs. These are elements of the scalar field Fr. /// @return x The X coordinate of the resulting G1 point. /// @return y The Y coordinate of the resulting G1 point. - function publicInputMSM(uint256[3] calldata input) - internal view returns (uint256 x, uint256 y) { + function publicInputMSM(uint256[3] calldata input) internal view returns (uint256 x, uint256 y) { // Note: The ECMUL precompile does not reject unreduced values, so we check this. // Note: Unrolling this loop does not cost much extra in code-size, the bulk of the // code-size is in the PUB_ constants. @@ -377,21 +379,21 @@ contract Verifier { mstore(add(f, 0x20), CONSTANT_Y) mstore(g, PUB_0_X) mstore(add(g, 0x20), PUB_0_Y) - s := calldataload(input) + s := calldataload(input) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) mstore(g, PUB_1_X) mstore(add(g, 0x20), PUB_1_Y) - s := calldataload(add(input, 32)) + s := calldataload(add(input, 32)) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) mstore(g, PUB_2_X) mstore(add(g, 0x20), PUB_2_Y) - s := calldataload(add(input, 64)) + s := calldataload(add(input, 64)) mstore(add(g, 0x40), s) success := and(success, lt(s, R)) success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) @@ -413,8 +415,7 @@ contract Verifier { /// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197. /// @return compressed The compressed proof. Elements are in the same order as for /// verifyCompressedProof. I.e. points (A, B, C) in compressed format. - function compressProof(uint256[8] calldata proof) - public view returns (uint256[4] memory compressed) { + function compressProof(uint256[8] calldata proof) public view returns (uint256[4] memory compressed) { compressed[0] = compress_g1(proof[0], proof[1]); (compressed[2], compressed[1]) = compress_g2(proof[3], proof[2], proof[5], proof[4]); compressed[3] = compress_g1(proof[6], proof[7]); @@ -429,13 +430,9 @@ contract Verifier { /// matching the output of compressProof. /// @param input the public input field elements in the scalar field Fr. /// Elements must be reduced. - function verifyCompressedProof( - uint256[4] calldata compressedProof, - uint256[3] calldata input - ) public view { + function verifyCompressedProof(uint256[4] calldata compressedProof, uint256[3] calldata input) public view { (uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]); - (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = decompress_g2( - compressedProof[2], compressedProof[1]); + (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = decompress_g2(compressedProof[2], compressedProof[1]); (uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]); (uint256 Lx, uint256 Ly) = publicInputMSM(input); @@ -444,17 +441,17 @@ contract Verifier { // Note: The pairing precompile rejects unreduced values, so we won't check that here. uint256[24] memory pairings; // e(A, B) - pairings[ 0] = Ax; - pairings[ 1] = Ay; - pairings[ 2] = Bx1; - pairings[ 3] = Bx0; - pairings[ 4] = By1; - pairings[ 5] = By0; + pairings[0] = Ax; + pairings[1] = Ay; + pairings[2] = Bx1; + pairings[3] = Bx0; + pairings[4] = By1; + pairings[5] = By0; // e(C, -δ) - pairings[ 6] = Cx; - pairings[ 7] = Cy; - pairings[ 8] = DELTA_NEG_X_1; - pairings[ 9] = DELTA_NEG_X_0; + pairings[6] = Cx; + pairings[7] = Cy; + pairings[8] = DELTA_NEG_X_1; + pairings[9] = DELTA_NEG_X_0; pairings[10] = DELTA_NEG_Y_1; pairings[11] = DELTA_NEG_Y_0; // e(α, -β) @@ -494,15 +491,12 @@ contract Verifier { /// of compressProof. /// @param input the public input field elements in the scalar field Fr. /// Elements must be reduced. - function verifyProof( - uint256[8] calldata proof, - uint256[3] calldata input - ) public view { + function verifyProof(uint256[8] calldata proof, uint256[3] calldata input) public view { (uint256 x, uint256 y) = publicInputMSM(input); // Note: The precompile expects the F2 coefficients in big-endian order. // Note: The pairing precompile rejects unreduced values, so we won't check that here. - + bool success; assembly ("memory-safe") { let f := mload(0x40) // Free memory pointer. diff --git a/contracts/test/verifiers/VerifierPlonk.sol b/contracts/test/verifiers/VerifierPlonk.sol index 97e97e6f1..31eb7512f 100644 --- a/contracts/test/verifiers/VerifierPlonk.sol +++ b/contracts/test/verifiers/VerifierPlonk.sol @@ -19,1003 +19,969 @@ pragma solidity ^0.8.19; contract PlonkVerifier { - - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 4983793384399042314514685285588169416118722874476172449762781616967620223650; - uint256 private constant g2_srs_1_x_1 = 6754759659809702461615589994680829062963468502059944990602949359395558725093; - uint256 private constant g2_srs_1_y_0 = 5331505949168118497261346664048967009937600438919278322717103563713059957147; - uint256 private constant g2_srs_1_y_1 = 17918899171254067306883902385521504405796104532858037361358570281963985230108; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 8; - uint256 private constant vk_inv_domain_size = 19152212512859365819465605027100115702479818850364030050735928663253832433665; - uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; - uint256 private constant vk_ql_com_x = 20205283157081902125528657762123841684827379648193571622345244782571495119686; - uint256 private constant vk_ql_com_y = 21781445325313964091213675703446620686011620905054551504505798925570055048063; - uint256 private constant vk_qr_com_x = 490962721355515306782896251905009642168493832672302100311478156385773223808; - uint256 private constant vk_qr_com_y = 5270239512471818815551793567649292792631076552073233008263432301253091160761; - uint256 private constant vk_qm_com_x = 0; - uint256 private constant vk_qm_com_y = 0; - uint256 private constant vk_qo_com_x = 19292694996756178462254602912706368102521676239672741174165871006523154949195; - uint256 private constant vk_qo_com_y = 4653594422812323089240491133537319932723564121642818450335715251388498984312; - uint256 private constant vk_qk_com_x = 0; - uint256 private constant vk_qk_com_y = 0; - - uint256 private constant vk_s1_com_x = 13984411499425252426979037306908471623629545478016463187874526256591009669144; - uint256 private constant vk_s1_com_y = 13329843082682862541917988893852836210961433899024857695150834771710136078858; - - uint256 private constant vk_s2_com_x = 21187293040598246119114144054694675047356712226870389216801786263069433808764; - uint256 private constant vk_s2_com_y = 17590548947506374380575927746142287295771745216104366314033343244050297407117; - - uint256 private constant vk_s3_com_x = 16640448690559526797085628912342799515609712408357461915381186345406083214266; - uint256 private constant vk_s3_com_y = 15440914661538149192522449501002430206648217584625716664820007542182414216623; - - uint256 private constant vk_coset_shift = 5; - - - - - - uint256 private constant vk_nb_commitments_commit_api = 0; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - - - 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_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) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { - error_proof_size() - } - } - - function check_proof_openings_size(aproof) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced)->beta_not_reduced{ - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { - 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} - { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr,0x180), vk_s1_com_x) - mstore(add(mPtr,0x1a0), vk_s1_com_y) - mstore(add(mPtr,0x1c0), vk_s2_com_x) - mstore(add(mPtr,0x1e0), vk_s2_com_y) - - let offset := 0x200 - - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { - 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := add( - aproof, - add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) - mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 4983793384399042314514685285588169416118722874476172449762781616967620223650; + uint256 private constant g2_srs_1_x_1 = 6754759659809702461615589994680829062963468502059944990602949359395558725093; + uint256 private constant g2_srs_1_y_0 = 5331505949168118497261346664048967009937600438919278322717103563713059957147; + uint256 private constant g2_srs_1_y_1 = + 17918899171254067306883902385521504405796104532858037361358570281963985230108; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 8; + uint256 private constant vk_inv_domain_size = + 19152212512859365819465605027100115702479818850364030050735928663253832433665; + uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; + uint256 private constant vk_ql_com_x = 20205283157081902125528657762123841684827379648193571622345244782571495119686; + uint256 private constant vk_ql_com_y = 21781445325313964091213675703446620686011620905054551504505798925570055048063; + uint256 private constant vk_qr_com_x = 490962721355515306782896251905009642168493832672302100311478156385773223808; + uint256 private constant vk_qr_com_y = 5270239512471818815551793567649292792631076552073233008263432301253091160761; + uint256 private constant vk_qm_com_x = 0; + uint256 private constant vk_qm_com_y = 0; + uint256 private constant vk_qo_com_x = 19292694996756178462254602912706368102521676239672741174165871006523154949195; + uint256 private constant vk_qo_com_y = 4653594422812323089240491133537319932723564121642818450335715251388498984312; + uint256 private constant vk_qk_com_x = 0; + uint256 private constant vk_qk_com_y = 0; + + uint256 private constant vk_s1_com_x = 13984411499425252426979037306908471623629545478016463187874526256591009669144; + uint256 private constant vk_s1_com_y = 13329843082682862541917988893852836210961433899024857695150834771710136078858; + + uint256 private constant vk_s2_com_x = 21187293040598246119114144054694675047356712226870389216801786263069433808764; + uint256 private constant vk_s2_com_y = 17590548947506374380575927746142287295771745216104366314033343244050297407117; + + uint256 private constant vk_s3_com_x = 16640448690559526797085628912342799515609712408357461915381186345406083214266; + uint256 private constant vk_s3_com_y = 15440914661538149192522449501002430206648217584625716664820007542182414216623; + + uint256 private constant vk_coset_shift = 5; + + uint256 private constant vk_nb_commitments_commit_api = 0; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + 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_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) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for { let i } lt(i, s) { i := add(i, 1) } { + input_checks := and(input_checks, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { error_inputs_size() } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api, 0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } + } + + function check_proof_openings_size(aproof) { + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { error_proof_openings_size() } + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced) -> beta_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { error_verify() } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore( + add(state, state_folded_claimed_values), + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) + ) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr, 0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr, 0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr, 0x180), vk_s1_com_x) + mstore(add(mPtr, 0x1a0), vk_s1_com_y) + mstore(add(mPtr, 0x1c0), vk_s2_com_x) + mstore(add(mPtr, 0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := + add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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 + ) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore( + computed_quotient, + addmod( + calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), + mload(add(state, state_pi)), + r_mod + ) + ) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore( + computed_quotient, + addmod( + mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod + ) + ) + mstore( + s2, + mulmod( + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)), + mload(add(state, state_zeta_power_n_minus_one)), + r_mod + ) + ) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) + if eq(check_staticcall, 0) { error_verify() } + res := mload(mPtr) + } } - res := mload(mPtr) - } } - } } diff --git a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol index 084b24ea0..c51208568 100644 --- a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol +++ b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol @@ -19,1175 +19,1130 @@ pragma solidity ^0.8.19; contract PlonkVerifier { - - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 4063571233546661467734799708300131386289831282909879383778035852630874856695; - uint256 private constant g2_srs_1_x_1 = 4647926734714541979239671019576593881493945862077482063229589788724791450632; - uint256 private constant g2_srs_1_y_0 = 15514169863847815805861354127833333882511661051872364096032336682739266504631; - uint256 private constant g2_srs_1_y_1 = 14981245089384445918999453598516659596327553649108599315427712934518517584091; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 64; - uint256 private constant vk_inv_domain_size = 21546239076966786546898805655487630165289796206659533807077919746160561487873; - uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; - uint256 private constant vk_ql_com_x = 8241117109803098541098613984488731009721160645379011473335652343529535266960; - uint256 private constant vk_ql_com_y = 20722965259158873821120253635967463269877724605143204728344162493690267204338; - uint256 private constant vk_qr_com_x = 6655659571137710427173880313823537029253828787683560125550266937179191868532; - uint256 private constant vk_qr_com_y = 13808918852189657493040000991883636660567905880489122692410877765981247442335; - uint256 private constant vk_qm_com_x = 9617414438893381633510036004229347488936383320496724899204335238610971458841; - uint256 private constant vk_qm_com_y = 9139778173198521885297402021941671824119034286476198203166267405240154924689; - uint256 private constant vk_qo_com_x = 6753944061897297345277777124783567586429547954466671056344964801972446730543; - uint256 private constant vk_qo_com_y = 18520936095139799918057373004179121242639392163222661288456393604460483299493; - uint256 private constant vk_qk_com_x = 15533273441448245872993712610675610998153353458964021815595706151938181224464; - uint256 private constant vk_qk_com_y = 7989411376990188833711571569796406054937132408812417153401882189030984814970; - - uint256 private constant vk_s1_com_x = 13250238937038458155524345632477738424994803733052110113126842346865903270013; - uint256 private constant vk_s1_com_y = 15799380157651776438753741634041125968174208046256469113223492112567454413031; - - uint256 private constant vk_s2_com_x = 16290959950757524941113235946220019026974774927006431669767544216586989016814; - uint256 private constant vk_s2_com_y = 12716127461505062001708899285893083752439690218620775280353810691937253438434; - - uint256 private constant vk_s3_com_x = 21721985565414851830858467827731205202151277206551566969624040213120314605650; - uint256 private constant vk_s3_com_y = 5607754375458556197432930148286768768082695251348209961717955451483607620014; - - uint256 private constant vk_coset_shift = 5; - - - uint256 private constant vk_selector_commitments_commit_api_0_x = 11452134746239496272478105026865109049072990374204527231978960641100599564380; - uint256 private constant vk_selector_commitments_commit_api_0_y = 12432732878880068425747106022374326794072801183995506113323301554838024086459; - - - uint256 private constant vk_index_commit_api_0 = 14; - - - uint256 private constant vk_nb_commitments_commit_api = 1; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - - // -------- utils (for hash_fr) - uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 - uint256 private constant zero_uint256 = 0; - - uint8 private constant lenInBytes = 48; - uint8 private constant sizeDomain = 11; - uint8 private constant one = 1; - uint8 private constant two = 2; - - - 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) - l_pi := addmod(l_wocommit, l_pi, r_mod) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { - error_proof_size() - } - } - - function check_proof_openings_size(aproof) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced)->beta_not_reduced{ - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - - // mPtr free memory. Computes the public input contribution related to the commit - 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_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) - - - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - 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) - - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr)->res { - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), 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), lenInBytes) - 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), sizeDomain) - - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { - 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} - { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr,0x180), vk_s1_com_x) - mstore(add(mPtr,0x1a0), vk_s1_com_y) - mstore(add(mPtr,0x1c0), vk_s2_com_x) - mstore(add(mPtr,0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) - mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) - offset := add(offset, 0x40) - - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - - let _mPtr := add(mPtr, add(offset, 0xe0)) - let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { - 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := add( - aproof, - add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) - mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 4063571233546661467734799708300131386289831282909879383778035852630874856695; + uint256 private constant g2_srs_1_x_1 = 4647926734714541979239671019576593881493945862077482063229589788724791450632; + uint256 private constant g2_srs_1_y_0 = + 15514169863847815805861354127833333882511661051872364096032336682739266504631; + uint256 private constant g2_srs_1_y_1 = + 14981245089384445918999453598516659596327553649108599315427712934518517584091; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 64; + uint256 private constant vk_inv_domain_size = + 21546239076966786546898805655487630165289796206659533807077919746160561487873; + uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; + uint256 private constant vk_ql_com_x = 8241117109803098541098613984488731009721160645379011473335652343529535266960; + uint256 private constant vk_ql_com_y = 20722965259158873821120253635967463269877724605143204728344162493690267204338; + uint256 private constant vk_qr_com_x = 6655659571137710427173880313823537029253828787683560125550266937179191868532; + uint256 private constant vk_qr_com_y = 13808918852189657493040000991883636660567905880489122692410877765981247442335; + uint256 private constant vk_qm_com_x = 9617414438893381633510036004229347488936383320496724899204335238610971458841; + uint256 private constant vk_qm_com_y = 9139778173198521885297402021941671824119034286476198203166267405240154924689; + uint256 private constant vk_qo_com_x = 6753944061897297345277777124783567586429547954466671056344964801972446730543; + uint256 private constant vk_qo_com_y = 18520936095139799918057373004179121242639392163222661288456393604460483299493; + uint256 private constant vk_qk_com_x = 15533273441448245872993712610675610998153353458964021815595706151938181224464; + uint256 private constant vk_qk_com_y = 7989411376990188833711571569796406054937132408812417153401882189030984814970; + + uint256 private constant vk_s1_com_x = 13250238937038458155524345632477738424994803733052110113126842346865903270013; + uint256 private constant vk_s1_com_y = 15799380157651776438753741634041125968174208046256469113223492112567454413031; + + uint256 private constant vk_s2_com_x = 16290959950757524941113235946220019026974774927006431669767544216586989016814; + uint256 private constant vk_s2_com_y = 12716127461505062001708899285893083752439690218620775280353810691937253438434; + + uint256 private constant vk_s3_com_x = 21721985565414851830858467827731205202151277206551566969624040213120314605650; + uint256 private constant vk_s3_com_y = 5607754375458556197432930148286768768082695251348209961717955451483607620014; + + uint256 private constant vk_coset_shift = 5; + + uint256 private constant vk_selector_commitments_commit_api_0_x = + 11452134746239496272478105026865109049072990374204527231978960641100599564380; + uint256 private constant vk_selector_commitments_commit_api_0_y = + 12432732878880068425747106022374326794072801183995506113323301554838024086459; + + uint256 private constant vk_index_commit_api_0 = 14; + + uint256 private constant vk_nb_commitments_commit_api = 1; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + // -------- utils (for hash_fr) + uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant zero_uint256 = 0; + + uint8 private constant lenInBytes = 48; + uint8 private constant sizeDomain = 11; + uint8 private constant one = 1; + uint8 private constant two = 2; + + 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for { let i } lt(i, s) { i := add(i, 1) } { + input_checks := and(input_checks, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { error_inputs_size() } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api, 0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } + } + + function check_proof_openings_size(aproof) { + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { error_proof_openings_size() } + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced) -> beta_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { error_verify() } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + // mPtr free memory. Computes the public input contribution related to the commit + 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_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + 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) + } + + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr) -> res { + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), 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), lenInBytes) + 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), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { error_verify() } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for { let i := 0 } lt(i, 0x10) { i := add(i, 1) } { + // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore( + add(state, state_folded_claimed_values), + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) + ) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr, 0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr, 0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr, 0x180), vk_s1_com_x) + mstore(add(mPtr, 0x1a0), vk_s1_com_y) + mstore(add(mPtr, 0x1c0), vk_s2_com_x) + mstore(add(mPtr, 0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr, offset), vk_selector_commitments_commit_api_0_x) + mstore(add(mPtr, add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) + offset := add(offset, 0x40) + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + let _mPtr := add(mPtr, add(offset, 0xe0)) + let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := + add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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 + ) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore( + computed_quotient, + addmod( + calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), + mload(add(state, state_pi)), + r_mod + ) + ) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore( + computed_quotient, + addmod( + mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod + ) + ) + mstore( + s2, + mulmod( + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)), + mload(add(state, state_zeta_power_n_minus_one)), + r_mod + ) + ) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) + if eq(check_staticcall, 0) { error_verify() } + res := mload(mPtr) + } } - res := mload(mPtr) - } } - } } From c50cbf54974a6c580edcc9526282763ef495cf5a Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Fri, 13 Oct 2023 14:22:26 -0700 Subject: [PATCH 19/30] upgraded to optimized version of gnark --- go.mod | 4 ++-- go.sum | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 803a4f51f..7fed10a4c 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/consensys/gnark-crypto v0.12.1 github.com/ethereum/go-ethereum v1.12.0 github.com/stretchr/testify v1.8.4 - github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec + github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b ) require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/bits-and-blooms/bitset v1.9.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect diff --git a/go.sum b/go.sum index 4742a2aa7..d063cd7ed 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/bits-and-blooms/bitset v1.9.0 h1:g1YivPG8jOtrN013Fe8OBXubkiTwvm7/vG2vXz03ANU= github.com/bits-and-blooms/bitset v1.9.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= @@ -96,6 +98,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec h1:xMlCVbabspLQ/sukwyu1WQzznh476x4O2/ee5X8igZ0= github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b h1:kcqBBMCEhDG6cNKAosD+7OG97jkyih9/G7GuNMrW6A8= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= From 1f82c01b97962fcca86196583c2a6b55c543fc91 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Fri, 13 Oct 2023 16:30:39 -0700 Subject: [PATCH 20/30] Test works --- contracts/test/verifiers/TestVerifier.t.sol | 36 ++++++++++++++++--- .../test/verifiers/function_proof_data.json | 6 ++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 contracts/test/verifiers/function_proof_data.json diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol index 4db0f5f66..bd50a982e 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -10,12 +10,15 @@ import {PlonkVerifier} from "./VerifierPlonk.sol"; import {PlonkVerifier as PlonkRangeCheckVerifier} from "./VerifierPlonkRangeCheck.sol"; import {VmSafe} from "forge-std/Vm.sol"; import {stdJson} from "forge-std/StdJson.sol"; +import {FunctionVerifier} from "./FunctionVerifier.sol"; contract VerifierTest is Test { function testVerifierGroth16() public { Groth16Verifier verifier = new Groth16Verifier(); - string memory groth16Json = vm.readFile("test/verifiers/groth16_proof_data.json"); + string memory groth16Json = vm.readFile( + "test/verifiers/groth16_proof_data.json" + ); uint256[] memory proof = stdJson.readUintArray(groth16Json, "$.proof"); uint256[] memory input = stdJson.readUintArray(groth16Json, "$.inputs"); @@ -33,16 +36,23 @@ contract VerifierTest is Test { uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); - uint256[4] memory compressedProof = verifier.compressProof(proofConverted); + uint256[4] memory compressedProof = verifier.compressProof( + proofConverted + ); startGas = gasleft(); verifier.verifyCompressedProof(compressedProof, inputConverted); endGas = gasleft(); - console.log("gas used for verifying compressed proof: %d", startGas - endGas); + console.log( + "gas used for verifying compressed proof: %d", + startGas - endGas + ); } function testVerifierPlonk() public { PlonkVerifier verifier = new PlonkVerifier(); - string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data.json"); + string memory proofJson = vm.readFile( + "test/verifiers/plonk_proof_data.json" + ); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); @@ -53,7 +63,9 @@ contract VerifierTest is Test { function testVerifierPlonkRangeCheck() public { PlonkRangeCheckVerifier verifier = new PlonkRangeCheckVerifier(); - string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data_range_check.json"); + string memory proofJson = vm.readFile( + "test/verifiers/plonk_proof_data_range_check.json" + ); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); @@ -61,4 +73,18 @@ contract VerifierTest is Test { uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); } + + function testVerifierFunction() public { + FunctionVerifier verifier = new FunctionVerifier(); + string memory proofJson = vm.readFile( + "test/verifiers/function_proof_data.json" + ); + bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); + bytes32 inputHash = stdJson.readBytes32(proofJson, "$.input_hash"); + bytes32 outputHash = stdJson.readBytes32(proofJson, "$.output_hash"); + uint256 startGas = gasleft(); + verifier.verify(inputHash, outputHash, proof); + uint256 endGas = gasleft(); + console.log("gas used: %d", startGas - endGas); + } } diff --git a/contracts/test/verifiers/function_proof_data.json b/contracts/test/verifiers/function_proof_data.json new file mode 100644 index 000000000..bee3b97db --- /dev/null +++ b/contracts/test/verifiers/function_proof_data.json @@ -0,0 +1,6 @@ +{ + "input_hash": "0x0b2871da34670fde248604e0f18fd3e4f7e1e6dfddb85875ce4813a6612953bb", + "output_hash": "0x07cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6", + "verifier_digest": "0x1c08790b2727738d6d86858ce7b584cb2a9f24b1911aac93ed5f66d9c01f0fda", + "proof": "0x1c727bc35e3aada620810beae257c96f394e05964373291b786577f7a7158b722f059d091a946faceacfaefb21d78bb81f0fe1b9c5008719f313a7713809cddf25416ab356a985fbe896c36a7e69fd63ad1a60113c3705e9cbcc6cd457327351257bd62c85bf00f2f8ad8e99b8e627d6215e3e1fec348dec503e3e18963ea1ed13979ba111e754644fb56e3614af63e67bfa6942398496730ee72ea6505471ff21a962d11da39d981818a66937041e693b0078111a2dda5e3c7737dea268aca121cbbdae287128441527ebbf9aba84d5b4c1d6e6129f9e2975801c731cc066e5268769235ebcfe288795333b0f53293d3ae6f4c9f52471907c3a8ba24340e2fb134e326a6669a7f7f695433a75e18e80040b1b824da3a390c96a77f5b05db44d0dfbbde8a82b47e71d250fba7453744375ed8c182e5d967eed949272b84581a7111bd233f42e658b1d8eba2fea19c9f82969176fba0b40c8013abea54cd29b830fc8c7ea3ee6e24697a0b984a64ff78468d5bda5a42ae1f83e7f083721197d3e14e43e8e3feb36d22b58158464817c8a3ccf840b588b340edf48fb2d060a37aa0126a0a2b6b1d5cdc7820a5f0afab9b705898bbc441795edae7d3a468fc572ec074013598d3b92d7b2def1d38cade7cdc5c7172c417cfe09f0dc541f04b1c36d0dfec80f151107ffe21509756b378e7c1a51da170ed2e6bc409683f105c9ac6c0542c601e99d9b36b166c30faffe770746811f3e23f2c4b27cec61a1f704352114eb6db0395cff9f74beafa5ff7c7ecd8fef0fbf43da5cbfd78ecd166f68d23b2e5bb9494557be40f12ff9e5711a9baee4def6abdcfe5f62e86b4b6ad479a7e61d04a446d1a215b4eb0997979afb09e16c3c42e84ad29113c327837ebfde3b7d1c8423e43363d877bba75c3bf1f93ced9bc5b42d00f071a74be43f577ff9d0251de1dc14d637db44b1cc01bb5a83bcd8de6d3b063bbf9f61c6731fc83455c098277c2fe9d0fd2ffc425539a3d36128258442610b6ddf96e736edce7917c198b60c39b86940fb56eeaffc5d0d24a4a219fa8abb5d87cea4b1f3d94d1f2eef64a2283a54d02e0b0360c4a055feabac4a79b85f0c15796a0a0c43cc802510ef7e371a6dc9a71f78c6e93f8ec15fb50bb40f92748411fda41ff0398a72e6d6c4949b185ce63c827e43c4542de512b12b84e66546d5a9c47576982ef95e50842e889b21d1e4407df939ce389bb4bdb686e42424e5e912906bb27f5d6ae1a9237fa38f27a47b3be6d655ba5a87fb3921e58324ceeec99d63ceee69bd0a15511ada78bb" +} \ No newline at end of file From 32e412bc8b994c216814bb6fed430633dfcd5c02 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Mon, 16 Oct 2023 17:12:19 -0700 Subject: [PATCH 21/30] With tons of debug statements --- plonky2x/core/examples/evm.rs | 34 +++++++++++++++++++---- plonky2x/core/src/backend/function/mod.rs | 6 ++++ plonky2x/core/src/backend/wrapper/wrap.rs | 19 ++++++++++++- succinct.json | 4 +-- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/plonky2x/core/examples/evm.rs b/plonky2x/core/examples/evm.rs index d490a0967..dcc7c0ca6 100644 --- a/plonky2x/core/examples/evm.rs +++ b/plonky2x/core/examples/evm.rs @@ -21,6 +21,7 @@ use plonky2x::backend::circuit::{Circuit, PlonkParameters}; use plonky2x::backend::function::Plonky2xFunction; use plonky2x::frontend::vars::ByteVariable; use plonky2x::prelude::CircuitBuilder; +use plonky2x::utils; #[derive(Debug, Clone)] struct SimpleAdditionCircuit; @@ -35,6 +36,7 @@ impl Circuit for SimpleAdditionCircuit { } fn main() { + utils::setup_logger(); // So that we can see `builder.watch` during proof generation time SimpleAdditionCircuit::entrypoint(); } @@ -42,7 +44,8 @@ fn main() { mod tests { use std::path::PathBuf; - use plonky2x::backend::config::DefaultParameters; + use plonky2x::backend::circuit::config::{DefaultParameters, Groth16WrapperParameters}; + use plonky2x::backend::wrapper::wrap::WrappedCircuit; use plonky2x::prelude::{GoldilocksField, PoseidonGoldilocksConfig}; use super::*; @@ -53,7 +56,7 @@ mod tests { #[test] fn test_circuit_function_evm() { let mut builder = CircuitBuilder::::new(); - SimpleCircuit::define(&mut builder); + SimpleAdditionCircuit::define(&mut builder); let circuit = builder.build(); let mut input = circuit.input(); input.evm_write::(0u8); @@ -66,8 +69,29 @@ mod tests { #[test] fn test_circuit_function_evm_input_json() { - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let path = format!("{}/examples/evm.json", root.display()); - Function::test::(path); + // let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // let path = format!("{}/examples/evm.json", root.display()); + // Function::test::(path); + } + + #[test] + fn test_evm_wrap() { + utils::setup_logger(); + + let mut builder = CircuitBuilder::::new(); + SimpleAdditionCircuit::define(&mut builder); + let circuit = builder.build(); + let mut input = circuit.input(); + input.evm_write::(2u8); + input.evm_write::(9u8); + let (proof, mut output) = circuit.prove(&input); + circuit.verify(&proof, &input, &output); + let xor = output.evm_read::(); + assert_eq!(xor, 11u8); + let wrapper: WrappedCircuit<_, _, 2> = + WrappedCircuit::::build(circuit); + let wrapped_proof = wrapper.prove(&proof).unwrap(); + // dummy_wrapped_proof.save(dummy_path).unwrap(); + println!("Saved dummy_circuit"); } } diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index b52c39634..a5542c0ec 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -172,7 +172,12 @@ impl Plonky2xFunction for C { args.wrapper_path ); + if let PublicInput::Bytes(input_bytes) = input { + info!("Input Bytes: {}", hex::encode(input_bytes)); + } + if let PublicOutput::Bytes(output_bytes) = output { + info!("Output Bytes: {:?}", hex::encode(output_bytes.clone())); // It's quite fast (~5-10 seconds) to rebuild the wrapped circuit. Because of this we // choose to rebuild here instead of loading from disk. let wrapped_circuit = @@ -209,6 +214,7 @@ impl Plonky2xFunction for C { let result: ProofResult = ProofResult::from_bytes(result_data.proof, output_bytes); let json = serde_json::to_string_pretty(&result).unwrap(); + info!("output.json:\n{}", json); let mut file = File::create("output.json").unwrap(); file.write_all(json.as_bytes()).unwrap(); info!("Successfully saved full result to disk at output.json."); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index 209eb50f6..30f2de13a 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -68,15 +68,24 @@ where .map(ByteVariable::from_targets) .collect::>(); + hash_builder.watch_slice(&input_bytes, "input_bytes"); + hash_builder.watch_slice(&output_bytes, "output_bytes"); + let input_hash = hash_builder.sha256(&input_bytes); let output_hash = hash_builder.sha256(&output_bytes); + hash_builder.watch(&input_hash, "input_hash"); + hash_builder.watch(&output_hash, "output_hash"); + // We must truncate the top 3 bits because in the gnark-plonky2-verifier, the input_hash // and output_hash are both represented as 1 field element in the BN254 field // to reduce on-chain verification costs. let input_hash_zeroed = hash_builder.mask_be_bits(input_hash, 3); let output_hash_zeroed = hash_builder.mask_be_bits(output_hash, 3); + hash_builder.watch(&input_hash_zeroed, "input_hash_truncated"); + hash_builder.watch(&output_hash_zeroed, "output_hash_truncated"); + let input_vars = input_hash_zeroed .as_bytes() .iter() @@ -89,6 +98,9 @@ where .map(|b| b.to_variable(&mut hash_builder)) .collect::>(); + hash_builder.watch_slice(&input_vars, "input_hash_truncated as vars"); + hash_builder.watch_slice(&output_vars, "output_hash_truncated as vars"); + // Write input_hash, output_hash to public_inputs // In the gnark-plonky2-verifier, these 64 bytes get summed to 2 field elements that // correspond to the input_hash and output_hash respectively as public inputs. @@ -278,13 +290,18 @@ mod tests { let path = format!("{}/test_circuit/", build_path); let dummy_path = format!("{}/dummy/", build_path); + // Create an inner circuit for verification let mut builder = CircuitBuilder::::new(); - let _ = builder.evm_read::(); + let a = builder.evm_read::(); + let b = builder.evm_read::(); + let c = builder.xor(a, b); + builder.evm_write(c); // Set up the dummy circuit and wrapper let dummy_circuit = builder.build(); let mut dummy_input = dummy_circuit.input(); dummy_input.evm_write::(0u8); + dummy_input.evm_write::(1u8); let (dummy_inner_proof, dummy_output) = dummy_circuit.prove(&dummy_input); dummy_circuit.verify(&dummy_inner_proof, &dummy_input, &dummy_output); println!("Verified dummy_circuit"); diff --git a/succinct.json b/succinct.json index ec3467d06..583ae4f64 100644 --- a/succinct.json +++ b/succinct.json @@ -5,8 +5,8 @@ "name": "evm", "framework": "plonky2x", "baseDir": ".", - "buildCommand": "cargo run --release --example evm build && mv ./target/release/examples/evm ./build/evm", - "proveCommand": "./build/evm prove input.json" + "buildCommand": "RUST_LOG=debug cargo run --release --example evm build && mv ./target/release/examples/evm ./build/evm", + "proveCommand": "RUST_LOG=debug ./build/evm prove input.json" }, { "name": "eth_call", From 876f7e2b4d59a31738cb2582744c81284dcb4c47 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Mon, 16 Oct 2023 17:49:21 -0700 Subject: [PATCH 22/30] Made go command have a cli with stdin for circuitPath --- plonky2x/verifier/cli.go | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/plonky2x/verifier/cli.go b/plonky2x/verifier/cli.go index 3ad7a3866..2d59cf318 100644 --- a/plonky2x/verifier/cli.go +++ b/plonky2x/verifier/cli.go @@ -1,9 +1,12 @@ package main import ( + "bufio" _ "embed" "flag" + "fmt" "os" + "strings" "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/logger" @@ -21,8 +24,7 @@ func main() { log := logger.Logger() if *circuitPath == "" { - log.Error().Msg("please specify a path to circuit dir (containing verifier_only_circuit_data and proof_with_public_inputs)") - os.Exit(1) + log.Info().Msg("no circuitPath flag found, so user must input circuitPath via stdin") } if *dataPath == "" { @@ -57,25 +59,39 @@ func main() { } if *proofFlag { - log.Info().Msg("loading the plonk proving key and circuit data") + log.Info().Msg("loading the plonk proving key, circuit data and verifying key") r1cs, pk, err := LoadProverData(*dataPath) if err != nil { log.Err(err).Msg("failed to load the verifier circuit") os.Exit(1) } - log.Info().Msg("creating the plonk verifier proof") - proof, publicWitness, err := Prove(*circuitPath, r1cs, pk) + vk, err := LoadVerifierKey(*dataPath) if err != nil { - log.Err(err).Msg("failed to create the proof") + log.Err(err).Msg("failed to load the verifier key") os.Exit(1) } - log.Info().Msg("loading the proof, verifying key and verifying proof") - vk, err := LoadVerifierKey(*dataPath) + // If the circuitPath is "" and not provided as part of the CLI flags, then we wait + // for user input. + if *circuitPath == "" { + log.Info().Msg("Waiting for user to provide circuitPath from stdin") + reader := bufio.NewReader(os.Stdin) + str, err := reader.ReadString('\n') + if err != nil { + log.Err(err).Msg("failed to parse the user provided circuitPath") + } + trimmed := strings.TrimSuffix(str, "\n") + circuitPath = &trimmed + } + + log.Info().Msg(fmt.Sprintf("Generating the proof with circuitPath %s", *circuitPath)) + proof, publicWitness, err := Prove(*circuitPath, r1cs, pk) if err != nil { - log.Err(err).Msg("failed to load the verifier key") + log.Err(err).Msg("failed to create the proof") os.Exit(1) } + + log.Info().Msg("Verifying proof") err = plonk.Verify(proof, vk, publicWitness) if err != nil { log.Err(err).Msg("failed to verify proof") From 9c9522ddc9e8dafd4bfa6a4c76252b83d817c55a Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Mon, 16 Oct 2023 18:15:26 -0700 Subject: [PATCH 23/30] Modified function/mod.rs to write to stdout --- plonky2x/core/src/backend/function/mod.rs | 59 ++++++++++++++++++----- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index a5542c0ec..46c2a7275 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -138,6 +138,22 @@ impl Plonky2xFunction for C { AlgebraicHasher, OuterParameters::Config: Serialize, { + let gnark_wrapper_process = if args.wrapper_path != "" { + // If the wrapper path is provided, then we know we will be wrapping the proof + let child_process = + std::process::Command::new(path::Path::new(&args.wrapper_path).join("verifier")) + .arg("-prove") + .arg("-data") + .arg(path::Path::new(&args.wrapper_path)) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) + .spawn() + .expect("Failed to start gnark wrapper process"); + Some(child_process) + } else { + None + }; + let mut generator_registry = HintRegistry::new(); let mut gate_registry = GateRegistry::new(); C::register_generators::(&mut generator_registry); @@ -187,23 +203,40 @@ impl Plonky2xFunction for C { .save("wrapped") .expect("failed to save wrapped proof"); - // Call go wrapper - let verifier_output = - std::process::Command::new(path::Path::new(&args.wrapper_path).join("verifier")) - .arg("-prove") - .arg("-circuit") - .arg("wrapped") - .arg("-data") - .arg(path::Path::new(&args.wrapper_path)) - .stdout(std::process::Stdio::inherit()) - .stderr(std::process::Stdio::inherit()) - .output() - .expect("failed to execute process"); - + // The gnark_wrapper_process should have been started + let mut gnark_wrapper_process = gnark_wrapper_process.unwrap(); + + let stdin = gnark_wrapper_process + .stdin + .as_mut() + .expect("Failed to open stdin of gnark wrapper"); + stdin + .write_all(b"wrapped\n") + .expect("Failed to write to stdin"); + let verifier_output = gnark_wrapper_process + .wait_with_output() + .expect("failed to execute process"); if !verifier_output.status.success() { panic!("verifier failed"); } + // // Call go wrapper + // let verifier_output = + // std::process::Command::new(path::Path::new(&args.wrapper_path).join("verifier")) + // .arg("-prove") + // .arg("-circuit") + // .arg("wrapped") + // .arg("-data") + // .arg(path::Path::new(&args.wrapper_path)) + // .stdout(std::process::Stdio::inherit()) + // .stderr(std::process::Stdio::inherit()) + // .output() + // .expect("failed to execute process"); + + // if !verifier_output.status.success() { + // panic!("verifier failed"); + // } + // Read result from gnark verifier let file = std::fs::File::open("proof.json").unwrap(); let rdr = std::io::BufReader::new(file); From f115978ca38056be5213bf2ac52dd1f03c789986 Mon Sep 17 00:00:00 2001 From: Uma Date: Tue, 17 Oct 2023 18:20:11 -0700 Subject: [PATCH 24/30] std pipe --- plonky2x/core/src/backend/function/mod.rs | 25 +++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 46c2a7275..5d6fa6d8c 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -147,6 +147,7 @@ impl Plonky2xFunction for C { .arg(path::Path::new(&args.wrapper_path)) .stdout(std::process::Stdio::inherit()) .stderr(std::process::Stdio::inherit()) + .stdin(std::process::Stdio::piped()) .spawn() .expect("Failed to start gnark wrapper process"); Some(child_process) @@ -189,11 +190,11 @@ impl Plonky2xFunction for C { ); if let PublicInput::Bytes(input_bytes) = input { - info!("Input Bytes: {}", hex::encode(input_bytes)); + info!("Input Bytes: 0x{}", hex::encode(input_bytes)); } if let PublicOutput::Bytes(output_bytes) = output { - info!("Output Bytes: {:?}", hex::encode(output_bytes.clone())); + info!("Output Bytes: 0x{}", hex::encode(output_bytes.clone())); // It's quite fast (~5-10 seconds) to rebuild the wrapped circuit. Because of this we // choose to rebuild here instead of loading from disk. let wrapped_circuit = @@ -206,10 +207,22 @@ impl Plonky2xFunction for C { // The gnark_wrapper_process should have been started let mut gnark_wrapper_process = gnark_wrapper_process.unwrap(); - let stdin = gnark_wrapper_process - .stdin - .as_mut() - .expect("Failed to open stdin of gnark wrapper"); + let mut stdin_opt = None; + while stdin_opt.is_none() { + stdin_opt = match gnark_wrapper_process.stdin.as_mut() { + Some(stdin) => { + info!("Got stdin of child process"); + Some(stdin) + } + None => { + info!("Failed to open stdin of gnark wrapper. Retrying..."); + std::thread::sleep(std::time::Duration::from_secs(1)); + None + } + }; + } + let stdin = stdin_opt.unwrap(); + stdin .write_all(b"wrapped\n") .expect("Failed to write to stdin"); From 5f297deebee48ed448f9440bd3860b423bead277 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Tue, 17 Oct 2023 18:21:09 -0700 Subject: [PATCH 25/30] try again --- contracts/test/verifiers/TestVerifier.t.sol | 7 +- contracts/test/verifiers/VerifierPlonk.sol | 1960 ++++++------- .../verifiers/VerifierPlonkRangeCheck.sol | 2436 +++++++++-------- .../test/verifiers/plonk_proof_data.json | 2 +- .../plonk_proof_data_range_check.json | 2 +- go.mod | 5 +- go.sum | 6 + plonky2x/core/src/backend/function/mod.rs | 17 - plonky2x/verifier/gnark_verifiers_test.go | 4 +- 9 files changed, 2326 insertions(+), 2113 deletions(-) diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol index bd50a982e..3f18cee44 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -40,6 +40,7 @@ contract VerifierTest is Test { proofConverted ); startGas = gasleft(); + verifier.verifyCompressedProof(compressedProof, inputConverted); endGas = gasleft(); console.log( @@ -56,7 +57,7 @@ contract VerifierTest is Test { bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); - verifier.Verify(proof, input); + require(verifier.Verify(proof, input)); uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); } @@ -69,7 +70,7 @@ contract VerifierTest is Test { bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); - verifier.Verify(proof, input); + require(verifier.Verify(proof, input)); uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); } @@ -83,7 +84,7 @@ contract VerifierTest is Test { bytes32 inputHash = stdJson.readBytes32(proofJson, "$.input_hash"); bytes32 outputHash = stdJson.readBytes32(proofJson, "$.output_hash"); uint256 startGas = gasleft(); - verifier.verify(inputHash, outputHash, proof); + require(verifier.verify(inputHash, outputHash, proof)); uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); } diff --git a/contracts/test/verifiers/VerifierPlonk.sol b/contracts/test/verifiers/VerifierPlonk.sol index 31eb7512f..dfe69e802 100644 --- a/contracts/test/verifiers/VerifierPlonk.sol +++ b/contracts/test/verifiers/VerifierPlonk.sol @@ -19,969 +19,1003 @@ pragma solidity ^0.8.19; contract PlonkVerifier { - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 4983793384399042314514685285588169416118722874476172449762781616967620223650; - uint256 private constant g2_srs_1_x_1 = 6754759659809702461615589994680829062963468502059944990602949359395558725093; - uint256 private constant g2_srs_1_y_0 = 5331505949168118497261346664048967009937600438919278322717103563713059957147; - uint256 private constant g2_srs_1_y_1 = - 17918899171254067306883902385521504405796104532858037361358570281963985230108; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 8; - uint256 private constant vk_inv_domain_size = - 19152212512859365819465605027100115702479818850364030050735928663253832433665; - uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; - uint256 private constant vk_ql_com_x = 20205283157081902125528657762123841684827379648193571622345244782571495119686; - uint256 private constant vk_ql_com_y = 21781445325313964091213675703446620686011620905054551504505798925570055048063; - uint256 private constant vk_qr_com_x = 490962721355515306782896251905009642168493832672302100311478156385773223808; - uint256 private constant vk_qr_com_y = 5270239512471818815551793567649292792631076552073233008263432301253091160761; - uint256 private constant vk_qm_com_x = 0; - uint256 private constant vk_qm_com_y = 0; - uint256 private constant vk_qo_com_x = 19292694996756178462254602912706368102521676239672741174165871006523154949195; - uint256 private constant vk_qo_com_y = 4653594422812323089240491133537319932723564121642818450335715251388498984312; - uint256 private constant vk_qk_com_x = 0; - uint256 private constant vk_qk_com_y = 0; - - uint256 private constant vk_s1_com_x = 13984411499425252426979037306908471623629545478016463187874526256591009669144; - uint256 private constant vk_s1_com_y = 13329843082682862541917988893852836210961433899024857695150834771710136078858; - - uint256 private constant vk_s2_com_x = 21187293040598246119114144054694675047356712226870389216801786263069433808764; - uint256 private constant vk_s2_com_y = 17590548947506374380575927746142287295771745216104366314033343244050297407117; - - uint256 private constant vk_s3_com_x = 16640448690559526797085628912342799515609712408357461915381186345406083214266; - uint256 private constant vk_s3_com_y = 15440914661538149192522449501002430206648217584625716664820007542182414216623; - - uint256 private constant vk_coset_shift = 5; - - uint256 private constant vk_nb_commitments_commit_api = 0; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - 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_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) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for { let i } lt(i, s) { i := add(i, 1) } { - input_checks := and(input_checks, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { error_inputs_size() } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api, 0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } - } - - function check_proof_openings_size(aproof) { - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { error_proof_openings_size() } - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced) -> beta_not_reduced { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { error_verify() } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore( - add(state, state_folded_claimed_values), - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) - ) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata( - add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma - ) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr, 0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr, 0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr, 0x180), vk_s1_com_x) - mstore(add(mPtr, 0x1a0), vk_s1_com_y) - mstore(add(mPtr, 0x1c0), vk_s2_com_x) - mstore(add(mPtr, 0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := - add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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 - ) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore( - computed_quotient, - addmod( - calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), - mload(add(state, state_pi)), - r_mod - ) - ) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore( - computed_quotient, - addmod( - mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod - ) - ) - mstore( - s2, - mulmod( - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)), - mload(add(state, state_zeta_power_n_minus_one)), - r_mod - ) - ) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- [s]src - function point_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) - if eq(check_staticcall, 0) { error_verify() } - res := mload(mPtr) - } + + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 14184380469316848647399916994526908634873673556047703492928820707587405857357; + uint256 private constant g2_srs_1_x_1 = 482275098823665719974591155934619915930048247564089153862907740325982636821; + uint256 private constant g2_srs_1_y_0 = 9081740555712312681676101658142837412124700599863904881497360479980542967997; + uint256 private constant g2_srs_1_y_1 = 10788505562695957146517509586265372291431743735546853483226752798469396098331; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 8; + uint256 private constant vk_inv_domain_size = 19152212512859365819465605027100115702479818850364030050735928663253832433665; + uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; + uint256 private constant vk_ql_com_x = 21146737814662248939271101735192940543382366687977110484636304065631052234661; + uint256 private constant vk_ql_com_y = 17244895415666211266680412689420596204100592899763916548999226920673373295795; + uint256 private constant vk_qr_com_x = 12034157544991483751879070773917816837151424951659313593179465195961958636762; + uint256 private constant vk_qr_com_y = 21128964842635530241689801368183970178962786540029862999283336804588522584697; + uint256 private constant vk_qm_com_x = 0; + uint256 private constant vk_qm_com_y = 0; + uint256 private constant vk_qo_com_x = 9690145773039397511522502225033489318603819532491614216035810227753062202328; + uint256 private constant vk_qo_com_y = 13758061819365328413779269411839899429431070095046925263255802702279536261884; + uint256 private constant vk_qk_com_x = 0; + uint256 private constant vk_qk_com_y = 0; + + uint256 private constant vk_s1_com_x = 17182197487722945203768515568014680528614078471464176366870022784845400646403; + uint256 private constant vk_s1_com_y = 20906636482132349356265024019355776966766221689385434048938253293206541987941; + + uint256 private constant vk_s2_com_x = 2847633030445395915601715055583454986756161565327551832016327694440474004530; + uint256 private constant vk_s2_com_y = 19207431948149911445928003407925263528194340126876487591340310230428671229550; + + uint256 private constant vk_s3_com_x = 19086081295379119079520891096686420879294280039419717609102045282826926824183; + uint256 private constant vk_s3_com_y = 6844673266018017607568220197100655211356735831371287938772913139123642785933; + + uint256 private constant vk_coset_shift = 5; + + + + + + uint256 private constant vk_nb_commitments_commit_api = 0; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + + + 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_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) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { + error_proof_size() + } + } + + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { + 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} + { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr,0x180), vk_s1_com_x) + mstore(add(mPtr,0x1a0), vk_s1_com_y) + mstore(add(mPtr,0x1c0), vk_s2_com_x) + mstore(add(mPtr,0x1e0), vk_s2_com_y) + + let offset := 0x200 + + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { + 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := add( + aproof, + add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) + ) + for { + let i := 0 + } lt(i, vk_nb_commitments_commit_api) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) + mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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,dst,0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst,src,s, mPtr) { + let state := mload(0x40) + 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(),6,mPtr,0x80,dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { + error_ec_op() + } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() } + res := mload(mPtr) + } } + } } diff --git a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol index c51208568..b24a598ef 100644 --- a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol +++ b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol @@ -19,1130 +19,1318 @@ pragma solidity ^0.8.19; contract PlonkVerifier { - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 4063571233546661467734799708300131386289831282909879383778035852630874856695; - uint256 private constant g2_srs_1_x_1 = 4647926734714541979239671019576593881493945862077482063229589788724791450632; - uint256 private constant g2_srs_1_y_0 = - 15514169863847815805861354127833333882511661051872364096032336682739266504631; - uint256 private constant g2_srs_1_y_1 = - 14981245089384445918999453598516659596327553649108599315427712934518517584091; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 64; - uint256 private constant vk_inv_domain_size = - 21546239076966786546898805655487630165289796206659533807077919746160561487873; - uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; - uint256 private constant vk_ql_com_x = 8241117109803098541098613984488731009721160645379011473335652343529535266960; - uint256 private constant vk_ql_com_y = 20722965259158873821120253635967463269877724605143204728344162493690267204338; - uint256 private constant vk_qr_com_x = 6655659571137710427173880313823537029253828787683560125550266937179191868532; - uint256 private constant vk_qr_com_y = 13808918852189657493040000991883636660567905880489122692410877765981247442335; - uint256 private constant vk_qm_com_x = 9617414438893381633510036004229347488936383320496724899204335238610971458841; - uint256 private constant vk_qm_com_y = 9139778173198521885297402021941671824119034286476198203166267405240154924689; - uint256 private constant vk_qo_com_x = 6753944061897297345277777124783567586429547954466671056344964801972446730543; - uint256 private constant vk_qo_com_y = 18520936095139799918057373004179121242639392163222661288456393604460483299493; - uint256 private constant vk_qk_com_x = 15533273441448245872993712610675610998153353458964021815595706151938181224464; - uint256 private constant vk_qk_com_y = 7989411376990188833711571569796406054937132408812417153401882189030984814970; - - uint256 private constant vk_s1_com_x = 13250238937038458155524345632477738424994803733052110113126842346865903270013; - uint256 private constant vk_s1_com_y = 15799380157651776438753741634041125968174208046256469113223492112567454413031; - - uint256 private constant vk_s2_com_x = 16290959950757524941113235946220019026974774927006431669767544216586989016814; - uint256 private constant vk_s2_com_y = 12716127461505062001708899285893083752439690218620775280353810691937253438434; - - uint256 private constant vk_s3_com_x = 21721985565414851830858467827731205202151277206551566969624040213120314605650; - uint256 private constant vk_s3_com_y = 5607754375458556197432930148286768768082695251348209961717955451483607620014; - - uint256 private constant vk_coset_shift = 5; - - uint256 private constant vk_selector_commitments_commit_api_0_x = - 11452134746239496272478105026865109049072990374204527231978960641100599564380; - uint256 private constant vk_selector_commitments_commit_api_0_y = - 12432732878880068425747106022374326794072801183995506113323301554838024086459; - - uint256 private constant vk_index_commit_api_0 = 14; - - uint256 private constant vk_nb_commitments_commit_api = 1; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - // -------- utils (for hash_fr) - uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 - uint256 private constant zero_uint256 = 0; - - uint8 private constant lenInBytes = 48; - uint8 private constant sizeDomain = 11; - uint8 private constant one = 1; - uint8 private constant two = 2; - - 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_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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) - l_pi := addmod(l_wocommit, l_pi, r_mod) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for { let i } lt(i, s) { i := add(i, 1) } { - input_checks := and(input_checks, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { error_inputs_size() } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api, 0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } - } - - function check_proof_openings_size(aproof) { - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { error_proof_openings_size() } - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced) -> beta_not_reduced { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { error_verify() } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - // mPtr free memory. Computes the public input contribution related to the commit - 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_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire 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) - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - 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) - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr) -> res { - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), 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), lenInBytes) - 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), sizeDomain) - - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(l_success) { error_verify() } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), 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), sizeDomain) // 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 || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), 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), sizeDomain) // 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), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for { let i := 0 } lt(i, 0x10) { i := add(i, 1) } { - // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - } - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore( - add(state, state_folded_claimed_values), - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) - ) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata( - add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma - ) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr, 0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr, 0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr, 0x180), vk_s1_com_x) - mstore(add(mPtr, 0x1a0), vk_s1_com_y) - mstore(add(mPtr, 0x1c0), vk_s2_com_x) - mstore(add(mPtr, 0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore(add(mPtr, offset), vk_selector_commitments_commit_api_0_x) - mstore(add(mPtr, add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) - offset := add(offset, 0x40) - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - let _mPtr := add(mPtr, add(offset, 0xe0)) - let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := - add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) - for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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 - ) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore( - computed_quotient, - addmod( - calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), - mload(add(state, state_pi)), - r_mod - ) - ) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore( - computed_quotient, - addmod( - mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod - ) - ) - mstore( - s2, - mulmod( - calldataload(add(aproof, proof_quotient_polynomial_at_zeta)), - mload(add(state, state_zeta_power_n_minus_one)), - r_mod - ) - ) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- [s]src - function point_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst, src, s, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { error_ec_op() } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) - if eq(check_staticcall, 0) { error_verify() } - res := mload(mPtr) - } + + uint256 private constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 7814172549830033238223606142675268636058600480250623615512765003821404917357; + uint256 private constant G2_SRS_1_X_1 = 13645852243604263853237508664752124496968745818264599716927438924652849501499; + uint256 private constant G2_SRS_1_Y_0 = 21307838360152462312711713684081168554005605915621702901401407170852500681590; + uint256 private constant G2_SRS_1_Y_1 = 21572117749168060448329421516439686064665732218582150646334081055061164833449; + + uint256 private constant G1_SRS_X = 1; + uint256 private constant G1_SRS_Y = 2; + + // ----------------------- vk --------------------- + uint256 private constant VK_NB_PUBLIC_INPUTS = 3; + uint256 private constant VK_DOMAIN_SIZE = 64; + uint256 private constant VK_INV_DOMAIN_SIZE = 21546239076966786546898805655487630165289796206659533807077919746160561487873; + uint256 private constant VK_OMEGA = 9088801421649573101014283686030284801466796108869023335878462724291607593530; + uint256 private constant VK_QL_COM_X = 10398584435073889797632122505093129513546316083460155042201669609474507761393; + uint256 private constant VK_QL_COM_Y = 11330777933755117102961799810839691375686334590014561389258521851457877230369; + uint256 private constant VK_QR_COM_X = 4890069554988477196605704578982437675387725852092351667744598097766173864387; + uint256 private constant VK_QR_COM_Y = 19349603681767160646759566177297921942938550747056136456536680869640643726362; + uint256 private constant VK_QM_COM_X = 15102395100711249652702189888971210884726661069253717232051424256238336296154; + uint256 private constant VK_QM_COM_Y = 8429507290274846664135226417545857237016221800833374220037179018375269797509; + uint256 private constant VK_QO_COM_X = 8308944175243810613431144809106337373933253400523298809621497139697227011081; + uint256 private constant VK_QO_COM_Y = 15017491915531595518283812274044246345396629679070431756435244459826623666365; + uint256 private constant VK_QK_COM_X = 3633008490673505938984875516723788205584619065077429967414171095484684355226; + uint256 private constant VK_QK_COM_Y = 5924043858531262811057741401548398852576813068998609832283300183984594490265; + + uint256 private constant VK_S1_COM_X = 1831950617253311161965086323296612321621431881366015012145754662717020653254; + uint256 private constant VK_S1_COM_Y = 12552778567595986457625193226638552613143112724414943417872328062638651381509; + + uint256 private constant VK_S2_COM_X = 9282332095318458810329272575154746196077473337194803812097690515423473875292; + uint256 private constant VK_S2_COM_Y = 4132295717165950971373345207419370482187113718476938372487185645861198998345; + + uint256 private constant VK_S3_COM_X = 5528050128912291205450410066466372880115605758861985145700038793768988564158; + uint256 private constant VK_S3_COM_Y = 5265831924970622321828286611341299461171200084467948717918107531865540077415; + + uint256 private constant VK_COSET_SHIFT = 5; + + + uint256 private constant VK_QCP_0_X = 21793598771508025947887671914700122097813101646208783481283278104736097325441; + uint256 private constant VK_QCP_0_Y = 19261568386046758090660517995286593584435610364418601346457053212061975687679; + + + uint256 private constant VK_INDEX_COMMIT_API0 = 14; + uint256 private constant VK_NB_CUSTOM_GATES = 1; + + // ------------------------------------------------ + + // offset proof + uint256 private constant PROOF_L_COM_X = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta) + uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA = 0x280; // t(zeta) + uint256 private constant PROOF_LINEARISED_POLYNOMIAL_AT_ZETA = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x2c0; // [Wzeta] + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2e0; + + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x300; + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x320; + + uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x340; + uint256 private constant PROOF_COMMITMENTS_WIRES_CUSTOM_GATES = 0x360; + + // -> 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 = 0x00; + uint256 private constant STATE_BETA = 0x20; + uint256 private constant STATE_GAMMA = 0x40; + uint256 private constant STATE_ZETA = 0x60; + + // reusable value + uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80; + + // commitment to H + uint256 private constant STATE_FOLDED_H_X = 0xa0; + uint256 private constant STATE_FOLDED_H_Y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0; + uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_X = 0x140; + uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x160; + + uint256 private constant STATE_PI = 0x180; + + uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1a0; + + uint256 private constant STATE_GAMMA_KZG = 0x1c0; + + uint256 private constant STATE_SUCCESS = 0x1e0; + uint256 private constant STATE_CHECK_VAR = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant STATE_LAST_MEM = 0x220; + + // -------- 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; + + + /// 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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_wocommit, l_pi, R_MOD) + mstore(add(mem, STATE_PI), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) { + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(calldataload(p), R_MOD)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + /// 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(0x340, 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) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // quotient polynomial at zeta + p := add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_L_AT_ZETA + p := add(aproof, PROOF_L_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_R_AT_ZETA + p := add(aproof, PROOF_R_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_O_AT_ZETA + p := add(aproof, PROOF_O_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_S1_AT_ZETA + p := add(aproof, PROOF_S1_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_S2_AT_ZETA + p := add(aproof, PROOF_S2_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA + p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // 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)} + { + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // 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, 0x67616d6d61) // "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, 0x62657461) // "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, 0x616C706861) // "alpha" + let _mPtr := add(mPtr, 0x20) + mstore(_mPtr, beta_not_reduced) + _mPtr := add(_mPtr, 0x20) + + // Bsb22Commitments + let proof_bsb_commitments := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + 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, 0x7a657461) // "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_COMMITMENTS_WIRES_CUSTOM_GATES) + + 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_API0), 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) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, STATE_SUCCESS)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, STATE_SUCCESS), res_pairing) + } + + /// @notice Fold the opening proofs at ζ: + /// * at state+state_folded_digest we store: [H] + γ[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_FOLDED_H_X))) + mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_FOLDED_H_Y))) + mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) + + point_acc_mul(state_folded_digests, add(state, STATE_LINEARISED_POLYNOMIAL_X), acc_gamma, mPtr) + fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + 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 poscaz := 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), poscaz, acc_gamma) + poscaz := add(poscaz, 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): + /// * ζ + /// * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + /// * [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: + /// * H(ζ) + /// * 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, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA))) + mstore(add(mPtr,0x40), mload(add(state, STATE_FOLDED_H_X))) + mstore(add(mPtr,0x60), mload(add(state, STATE_FOLDED_H_Y))) + mstore(add(mPtr,0x80), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) + mstore(add(mPtr,0xa0), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, PROOF_L_COM_X), 0xc0) + mstore(add(mPtr,0x180), VK_S1_COM_X) + mstore(add(mPtr,0x1a0), VK_S1_COM_Y) + mstore(add(mPtr,0x1c0), VK_S2_COM_X) + mstore(add(mPtr,0x1e0), VK_S2_COM_Y) + + let offset := 0x200 + + 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), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_L_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_R_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_O_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) + + let _mPtr := add(mPtr, add(offset, 0xe0)) + + let _poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + for {let i:=0} lt(i, VK_NB_CUSTOM_GATES) {i:=add(i,1)} + { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + + mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x17, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x17 (zeta+2*7+7 for the digests+openings) + 2*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + VK_NB_CUSTOM_GATES (for the openings of the selectors) + 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 commits_api_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + let commits_api := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + for { + let i := 0 + } lt(i, VK_NB_CUSTOM_GATES) { + i := add(i, 1) + } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + /// @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] + /// 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)) + /// @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 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) + } + + /// @notice check that + /// L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + /// α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + /// + α²*L₁(ζ) = + /// (ζⁿ-1)H(ζ) + /// @param aproof pointer to the proof + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), STATE_LAST_MEM) + mstore(s1, mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) + mstore(s1, addmod(mload(s1), mload(add(state, STATE_GAMMA)), R_MOD)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) + mstore(s2, addmod(mload(s2), mload(add(state, STATE_GAMMA)), R_MOD)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), R_MOD)) + mstore(s1, mulmod(mload(s1), mload(o), R_MOD)) + mstore(s1, mulmod(mload(s1), mload(add(state, STATE_ALPHA)), R_MOD)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient,addmod(calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)), mload(add(state, STATE_PI)), R_MOD)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), R_MOD)) + mstore(computed_quotient,addmod(mload(computed_quotient), sub(R_MOD, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))), R_MOD)) + mstore(s2,mulmod(calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)),mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)),R_MOD)) + + mstore(add(state, STATE_SUCCESS), eq(mload(computed_quotient), mload(s2))) + } + + // 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) { + let state := mload(0x40) + 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(),6,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) { + let state := mload(0x40) + 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(), 6, 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) { + let state := mload(0x40) + 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,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) { + 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, 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) { + let state := mload(0x40) + 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(),6,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(), 6, 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(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() } + res := mload(mPtr) + } } + } } diff --git a/contracts/test/verifiers/plonk_proof_data.json b/contracts/test/verifiers/plonk_proof_data.json index a08a9ca84..fe70d56b5 100644 --- a/contracts/test/verifiers/plonk_proof_data.json +++ b/contracts/test/verifiers/plonk_proof_data.json @@ -1,5 +1,5 @@ { - "proof": "0x15635310727794e740080e2969e5fc69425cbbac5b8f45297df9040a68d1d84830301af25143705dbb92c4b16da98ead08afdf864a582e3bb7b26c49e6802e791c861250ad0d5e24c727b07d1d521d21de0f69534d4829f051ed50e2275af8b02f55b3c6d18d8cf8fbeebb9aed93a23691ed8935741f72d4941ede14c2eab0bc03076aed3f5339027208bd0948717ec26b968823a4cf4a8fa487fe7fe29e13f72dc3a15f7532a782f5b5d9c8511ceb4028003d6c051d3a78bdd0abc93860d46a2e570a7fbed1cfc59bc57464e1b6e9468f0c3e3d30ab5236e6b5d1ff7eee2f4229bc8b1cda05a2a2890b138cabcd0026e10748999e86e90570cb6ae7c2d581701e1440691f96a88dfb6a64ea35346f06bc5342549b388be0c76bf090877258132332300874657f062558e1716e44dd4cd7fbf498d54e54eab8302f9bcea6fd1e07c59cf6ee9729147cc20c993ee90b5c35a85b446e625baac4dd26033bb23298294935d52f60d741dd9349ffd1236d734b190d7ee51d4af7612b0625c1850d001fa390f28b1ee7b9b2635b26b6df9337b2114e032d64128fc051e9de825c5d4228f53084ab96f8d588404f6cac17e5897d5f36ffbf1001e810c8811a074bc62b1f9b5a0bf99ada3a59e58aaf70506d3bf47ca3ab3956ba2a0113d639c6e104f1131b166c09e2780518315517eecb60c9e5402aec675b1db652d981beac86f1890b2483c9c92980e0d3bd45e2757668a72979d5ce62d64e0e9e6bbbfa5ecc64872fdb7a92f70cc2626a5274221525243082a1274bfe83a3caec7bc4a17a9dcbeb25548d0d4945f1c71ba1654fcd63d7481dd85ee0210afe7fd45d6c0fc39d187f009018842e0a6870ee1a8e73614210906ce37c3d9960ecb7630902d175a7eb970fbc83270a2706e2ee9532d7c833d8508bc5507cf4959048b5fbffb58130d4a407a0b5fc9f61cfbf8ef37bfe71e2d0149923bd7eebad69138969aad2de036997019707865d800d8772a9970400e44a094fb604250bb718629218c84d505a68932e612cba931750444fe8da78e824f190619a2869640db4c1fbd3e9c8a6e8b19625402740f9a2d6aeae8736cb69ad95c9048bfa09a94c33ba4d70820759bfb29f22d309a3a479bb09100115ac8087858d339429c2fe475c2eb4d24aa3ac994b8e", + "proof": "0x2bb41ca5444b1b337923d281209f5db0f396b90495945aacf85aee51e449e7bb194c1bbce08ed0cc5899404ade8f12b989a2479d814cee5ca0f91a8251586d7e21ffe62920f0f10c94328e8a4e300fc2fc6c1af7d18c8635ce3e9b767d83215f278f01248bf8d8b6421cf4928b492268c84c7d38e0499567dad07382dcd4e07a2c7570c8feab462b2a50120abfb9b6fc3d413a32630dfec7bb202cd93522b36a1cdd6857e3c6d631cc9874bc508749520867c77feb4d103679cd146f69174f5d1b4b3fa9d913ed6d5b54fdfb5c7f0845c409a3d11f7120ffd36a2e3e7e588aab2b9b3aba5e40a874791db2cd455c6e18e9384da726922aa57d707334c818ab1c0617742417c36d4fa15dd14c895e0cd4369465a499822eae156b2c62a4db03210e6c823f0a6b80e35dd0b57a1fec0dd540c5fac1e2fc249798d68cd009927456222f1a4a2b79a23e1d3e95d750c004d3caaf9126c564c35c00ce0f91d3613d740dc26b677922e83c143c4731e8eb103489dbda2d948b8d78048b25a8007a45d31b274b754986f5ca67d90ec9d4d17609a8aec17d87f492690014b53262937bf8241de417a725a33de518ec538bb1210d113c88933671c4e1fc06d9323327bdb2054b630d9061acbdfb947eb9c741224831ef885ef2f1396b49df390b7437065a23271b2297473ed6f8216737ebbbc6fabdb074cd7fbbddbe2883ee9b65d78a2624df8f5e9cacd292a90831a733309e4ac35a2d5b7b5a594c544cc6ed00da327b2db55e88d8d488e2f2c5ea69b1fc65d30c349d964b1f5fe793de0ec961b32d041a20829792ce6ef8c49dc7e30b897a816aba9c98f8ac6d13f39714198ad853d321c2ee28dddedd1ea1eea2278de9a87fb81a64001462c6445ff448c9247673fd07e5a612b5d0ccbdadd4b183ca1f03264e5027b8d3463954cd708eecf0202a381b528b99597fd4dd78fb2b2cb661d96c254ed8742d2ab5270b1d7a43af0a51842f8bf610d224504efdc91c6deb51f42465bfe6da9206fffb8cb9a4c73f933f4c2003edeb62b990b17f67975d97f383bb15e6448b82f7e5859647c1e6027ed5af2a0ff51843e7244b96153ee21791ef53d8134ed4e815596152fab7357941d5582fc4a9d0ff32d889cda8e56de6e7f27a8ddfa9e66b1ec9f53a2cc99050d9a745", "inputs": [ "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000002", diff --git a/contracts/test/verifiers/plonk_proof_data_range_check.json b/contracts/test/verifiers/plonk_proof_data_range_check.json index 5d96abc8d..07acb158e 100644 --- a/contracts/test/verifiers/plonk_proof_data_range_check.json +++ b/contracts/test/verifiers/plonk_proof_data_range_check.json @@ -1,5 +1,5 @@ { - "proof": "0x1a209cbdaee542d883cb031d1d115262a938853130254d0bb98e29d45eeb505227b1187953d73bcbd43128c232b99840a06fc24c30d0b62f2c4ade45400b34be0fb094ae2cba56a816146c7c83e8d9966c7f0ff5c8df7054e80d0a5d20dc4b42059d0d37ad3eb9e9250b98d3df0dfd4588097c3d70172f12c6f745d5fd52066a113468580dabb552b5de7ba351db863a20e1832521d1165c115cc56a1dd320211fff83295b457af36e51d2bddc13f43a134f84a52104594409a433cedabab57910b6b102521889d140b8121c7d4f2eeb1b260f58e772c9606d77a71fcba09b69285148cfd1d120420c78d0fad4451bb5bef6d767dc445bac8e3d5c14a619714412444dab59cb2807b0bbb6fb09a7af9c0184d1dcf39fdf8dc21ed389f128f8462a89cecb7f70ecd0ea0bc2109a0d2cca692ad73d9a609d57f3c9eaa9e4c33e8d07fccfa5e1f4a3be2f132b09b20f2867254dd42e2d8584ccbaa197a2369cca371e616747bc5586f58210007d6787e673e73eff25b741c3c49ded1f5360e77cb81382bcbfe306b42198fa42620447bb9cad3f552883cbd40e9f32b3e0d994b7601646efe00613c5dd7342f14f7cde3826be36de663fedc2be3b78c4f859d476b12ba5661f4c0419b9e1bab5a419fc1f8f21281920ef1b40cf63d849e2d89f79fd294898f8c44efa73fdef74e3cdbcc1f6374dd8f539e16d8d7468058aa8fea8ca146db1b01353a2a3871a0b2736ccc60a6cb201a3199dbd48acd6d95e8e6f99222120bdcc4f1c35a0181247219404274ef333f99c01c843e4981dfc8dfc6e57c62d45adf96d7cb6912a6b68ef26400d58604852f7046253dd980c8711f14a9e2d07df5efc45bd9079f78bb971d25291793db219105d3cff1deba34343fc653e230796603cc93efd7bee1d043d45cf42f7e6e280b976f71190156624668c4c591900f7cf3f08a6dc670740133382894d87ee11efca08fcc5ea8aba2f213b1f9d9e0ce4e523a6a0029943b141560b35fd892103d53d3d221828eecc60b60fb08e0f1e3872cb287096124e5e58bff91935a577d8cacf4f827fce2ad3c055aefc092200abbefec71ac1c99cd6f431b9baa3111523947a4013fa1e215573f704c5094a058adfe9298c02a4b7cf2e48177a8d0537af7d5166fc7e27b1ba408468615eb61b1436373b86bacfa8cbbe8040368f390ac84c9c9ed9e23b183bf458134e75022f2f21cf2a6d83f51f2b32adee91e36a0a678d0bfd12ddf141fe3115be40422600b0f02b50ad27b72856d06e6124c75e25beb6c679db800d22d95e3e0fbe7fde", + "proof": "0x04f69dae5b57e250c55e69cbf7851bd8f8281c33ea3a3b1f7f542435e53974020b39b71aaae0f74c806930a950c2656ad68f63fa14a48552c9d72c589132e46e2e83e98551a3df7b627a91c0ee9fb91b36259ea9e66f4ec7b00c7d05c6e3f2d62763fa8c38572c700066154ffa5a3ecb05c71e47be5648d477c76137da2e851f201bb814d9e467def4b004ed93a1fd6a5c44629f26c3cc25c93bf81b509348ff1c8bcb70e30d20ef3dad9f83b3e4980fa511b0f34a6fedaf286d6345801e4cb104fda64ff8c4ebcce05bdb2da86db2fb5abd0aff11602dae11bb92f2500d100b0c756decac031120c28e20eb3dd762117c1d3512cc92e0cafafba2d3c39a773211f4d71f45f9d5e78174f3997d3822df9983e2055653973888de3ba0ecfe2789161731d148d224b8f66c250e56cf23eeb8bb2d2fe94b3a79a624fc7e7d0cb88a0e0e0f56ae19e0dda68e870f76c0a4b8228e3907316612d42177e49130a6336d03245da21346ffdca508950418b704af1e583711f6696deae7bfa4761f2137f5033dce1e9bfc0a7a62cfb2cf24f0fa1d71beaefdba6306317ec0bc85f88e5a340ae6b0d287ca5d72a94d038bae46cbc7ec8d4eec980e96f3da8ca2fc818b3a360ae8e27f4597e81574c84d644d6a8b2413db4eaf1cc7f7dc3be1cb28618234b3229479c5f7564432a48898051087a47e6813d5b72d3c0aa3aa934ae56e4d82bd1d73033c9d25fc5f8c5bc79230630da0c415144dcb86ea3a89250fb030d67a15085664436bc0544bb7115965621db641fe91d3cb32aa5a2a9333dfd63bc817cb286c2f5dcb8511e85b8bcf3644f89da2ff68aecfe40ff82e876ec72327bf4d691bb360958bcea663787b1546aca23566433f30a2cb3484892672dc4fd93ed8e81c1ca8542d6cb19480e3679036ab92da0284b196c0aa3e90b25dabc91dd5db271f86586944b7d46d2dfd09688efdc6a65ead3c34e4144071098c0279d1e6f89c260c28f6fb6fd63c48d62ac45adbadd7b3bb0ae362520e2cd127bfca6cfa6c771854fb7fab3e993b983ba390d0a7d4857395794b7ff00e353b16151050c8c4502ca3ce881d1fc41cc932c999b28d3f040f77870f10d827f87c383b794932a0351c9c519d8185446b0750f50947ff6bdcfeb12cf5f22fa998095bc011505815201fa599d77cba37a3da3fc19140896242f7179b2b0c1b53b0eca8364cbc9a27800dcc6cd8c7b5012fa748f14a7b2b6ac87af2cf4fb786cac905ee2d738f65adfe180e8b78d33f6f5d2c7d97e4a31c637a2252ce8e863c186fdb63781ae3ac3d18", "inputs": [ "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000002", diff --git a/go.mod b/go.mod index 7fed10a4c..7b708c9ce 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/succinctlabs/sdk go 1.20 require ( - github.com/consensys/gnark v0.9.0 - github.com/consensys/gnark-crypto v0.12.1 + github.com/consensys/gnark v0.9.1 + github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb github.com/ethereum/go-ethereum v1.12.0 github.com/stretchr/testify v1.8.4 github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b @@ -38,6 +38,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.13.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d063cd7ed..7da9703ee 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,12 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark v0.9.0 h1:OoOr0Q771mQINVdP3s1AF2Rs1y8gtXhWVkadz/9KmZc= github.com/consensys/gnark v0.9.0/go.mod h1:Sy9jJjIaGJFfNeupyNOR9Ei2IbAB6cfCO78DfG27YvM= +github.com/consensys/gnark v0.9.1 h1:aTwBp5469MY/2jNrf4ABrqHRW3+JytfkADdw4ZBY7T0= +github.com/consensys/gnark v0.9.1/go.mod h1:udWvWGXnfBE7mn7BsNoGAvZDnUhcONBEtNijvVjfY80= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= +github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -113,6 +117,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index 5d6fa6d8c..a8d7024da 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -233,23 +233,6 @@ impl Plonky2xFunction for C { panic!("verifier failed"); } - // // Call go wrapper - // let verifier_output = - // std::process::Command::new(path::Path::new(&args.wrapper_path).join("verifier")) - // .arg("-prove") - // .arg("-circuit") - // .arg("wrapped") - // .arg("-data") - // .arg(path::Path::new(&args.wrapper_path)) - // .stdout(std::process::Stdio::inherit()) - // .stderr(std::process::Stdio::inherit()) - // .output() - // .expect("failed to execute process"); - - // if !verifier_output.status.success() { - // panic!("verifier failed"); - // } - // Read result from gnark verifier let file = std::fs::File::open("proof.json").unwrap(); let rdr = std::io::BufReader::new(file); diff --git a/plonky2x/verifier/gnark_verifiers_test.go b/plonky2x/verifier/gnark_verifiers_test.go index d1698c1d3..a53ef07f5 100644 --- a/plonky2x/verifier/gnark_verifiers_test.go +++ b/plonky2x/verifier/gnark_verifiers_test.go @@ -33,7 +33,7 @@ type MyCircuit struct { func (circuit *MyCircuit) Define(api frontend.API) error { api.AssertIsEqual(circuit.Z, api.Add(circuit.X, circuit.Y)) - if circuit.DoRangeCheck { + if true || circuit.DoRangeCheck { rangeChecker := rangecheck.New(api) rangeChecker.Check(circuit.X, 8) } @@ -134,7 +134,7 @@ type PlonkProofData struct { func TestPlonk(t *testing.T) { - range_check := false + range_check := true circuit := MyCircuit{DoRangeCheck: range_check} From 896c471133502ea0874210cb4bddd77b887803bc Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 18 Oct 2023 00:53:25 -0700 Subject: [PATCH 26/30] Cleanup test --- contracts/test/verifiers/TestVerifier.t.sol | 15 --------------- contracts/test/verifiers/function_proof_data.json | 6 ------ 2 files changed, 21 deletions(-) delete mode 100644 contracts/test/verifiers/function_proof_data.json diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol index 3f18cee44..6760c013e 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -10,7 +10,6 @@ import {PlonkVerifier} from "./VerifierPlonk.sol"; import {PlonkVerifier as PlonkRangeCheckVerifier} from "./VerifierPlonkRangeCheck.sol"; import {VmSafe} from "forge-std/Vm.sol"; import {stdJson} from "forge-std/StdJson.sol"; -import {FunctionVerifier} from "./FunctionVerifier.sol"; contract VerifierTest is Test { function testVerifierGroth16() public { @@ -74,18 +73,4 @@ contract VerifierTest is Test { uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); } - - function testVerifierFunction() public { - FunctionVerifier verifier = new FunctionVerifier(); - string memory proofJson = vm.readFile( - "test/verifiers/function_proof_data.json" - ); - bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); - bytes32 inputHash = stdJson.readBytes32(proofJson, "$.input_hash"); - bytes32 outputHash = stdJson.readBytes32(proofJson, "$.output_hash"); - uint256 startGas = gasleft(); - require(verifier.verify(inputHash, outputHash, proof)); - uint256 endGas = gasleft(); - console.log("gas used: %d", startGas - endGas); - } } diff --git a/contracts/test/verifiers/function_proof_data.json b/contracts/test/verifiers/function_proof_data.json deleted file mode 100644 index bee3b97db..000000000 --- a/contracts/test/verifiers/function_proof_data.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "input_hash": "0x0b2871da34670fde248604e0f18fd3e4f7e1e6dfddb85875ce4813a6612953bb", - "output_hash": "0x07cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6", - "verifier_digest": "0x1c08790b2727738d6d86858ce7b584cb2a9f24b1911aac93ed5f66d9c01f0fda", - "proof": "0x1c727bc35e3aada620810beae257c96f394e05964373291b786577f7a7158b722f059d091a946faceacfaefb21d78bb81f0fe1b9c5008719f313a7713809cddf25416ab356a985fbe896c36a7e69fd63ad1a60113c3705e9cbcc6cd457327351257bd62c85bf00f2f8ad8e99b8e627d6215e3e1fec348dec503e3e18963ea1ed13979ba111e754644fb56e3614af63e67bfa6942398496730ee72ea6505471ff21a962d11da39d981818a66937041e693b0078111a2dda5e3c7737dea268aca121cbbdae287128441527ebbf9aba84d5b4c1d6e6129f9e2975801c731cc066e5268769235ebcfe288795333b0f53293d3ae6f4c9f52471907c3a8ba24340e2fb134e326a6669a7f7f695433a75e18e80040b1b824da3a390c96a77f5b05db44d0dfbbde8a82b47e71d250fba7453744375ed8c182e5d967eed949272b84581a7111bd233f42e658b1d8eba2fea19c9f82969176fba0b40c8013abea54cd29b830fc8c7ea3ee6e24697a0b984a64ff78468d5bda5a42ae1f83e7f083721197d3e14e43e8e3feb36d22b58158464817c8a3ccf840b588b340edf48fb2d060a37aa0126a0a2b6b1d5cdc7820a5f0afab9b705898bbc441795edae7d3a468fc572ec074013598d3b92d7b2def1d38cade7cdc5c7172c417cfe09f0dc541f04b1c36d0dfec80f151107ffe21509756b378e7c1a51da170ed2e6bc409683f105c9ac6c0542c601e99d9b36b166c30faffe770746811f3e23f2c4b27cec61a1f704352114eb6db0395cff9f74beafa5ff7c7ecd8fef0fbf43da5cbfd78ecd166f68d23b2e5bb9494557be40f12ff9e5711a9baee4def6abdcfe5f62e86b4b6ad479a7e61d04a446d1a215b4eb0997979afb09e16c3c42e84ad29113c327837ebfde3b7d1c8423e43363d877bba75c3bf1f93ced9bc5b42d00f071a74be43f577ff9d0251de1dc14d637db44b1cc01bb5a83bcd8de6d3b063bbf9f61c6731fc83455c098277c2fe9d0fd2ffc425539a3d36128258442610b6ddf96e736edce7917c198b60c39b86940fb56eeaffc5d0d24a4a219fa8abb5d87cea4b1f3d94d1f2eef64a2283a54d02e0b0360c4a055feabac4a79b85f0c15796a0a0c43cc802510ef7e371a6dc9a71f78c6e93f8ec15fb50bb40f92748411fda41ff0398a72e6d6c4949b185ce63c827e43c4542de512b12b84e66546d5a9c47576982ef95e50842e889b21d1e4407df939ce389bb4bdb686e42424e5e912906bb27f5d6ae1a9237fa38f27a47b3be6d655ba5a87fb3921e58324ceeec99d63ceee69bd0a15511ada78bb" -} \ No newline at end of file From ea2db3bdb11ee50c4258407c94ecec15cd9b52d2 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 18 Oct 2023 00:55:03 -0700 Subject: [PATCH 27/30] Forge fmt --- contracts/test/verifiers/TestVerifier.t.sol | 21 +- contracts/test/verifiers/VerifierPlonk.sol | 1961 ++++++------- .../verifiers/VerifierPlonkRangeCheck.sol | 2574 ++++++++--------- 3 files changed, 2232 insertions(+), 2324 deletions(-) diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/TestVerifier.t.sol index 6760c013e..2c9100d39 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/TestVerifier.t.sol @@ -15,9 +15,7 @@ contract VerifierTest is Test { function testVerifierGroth16() public { Groth16Verifier verifier = new Groth16Verifier(); - string memory groth16Json = vm.readFile( - "test/verifiers/groth16_proof_data.json" - ); + string memory groth16Json = vm.readFile("test/verifiers/groth16_proof_data.json"); uint256[] memory proof = stdJson.readUintArray(groth16Json, "$.proof"); uint256[] memory input = stdJson.readUintArray(groth16Json, "$.inputs"); @@ -35,24 +33,17 @@ contract VerifierTest is Test { uint256 endGas = gasleft(); console.log("gas used: %d", startGas - endGas); - uint256[4] memory compressedProof = verifier.compressProof( - proofConverted - ); + uint256[4] memory compressedProof = verifier.compressProof(proofConverted); startGas = gasleft(); verifier.verifyCompressedProof(compressedProof, inputConverted); endGas = gasleft(); - console.log( - "gas used for verifying compressed proof: %d", - startGas - endGas - ); + console.log("gas used for verifying compressed proof: %d", startGas - endGas); } function testVerifierPlonk() public { PlonkVerifier verifier = new PlonkVerifier(); - string memory proofJson = vm.readFile( - "test/verifiers/plonk_proof_data.json" - ); + string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); @@ -63,9 +54,7 @@ contract VerifierTest is Test { function testVerifierPlonkRangeCheck() public { PlonkRangeCheckVerifier verifier = new PlonkRangeCheckVerifier(); - string memory proofJson = vm.readFile( - "test/verifiers/plonk_proof_data_range_check.json" - ); + string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data_range_check.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); diff --git a/contracts/test/verifiers/VerifierPlonk.sol b/contracts/test/verifiers/VerifierPlonk.sol index dfe69e802..b3851d848 100644 --- a/contracts/test/verifiers/VerifierPlonk.sol +++ b/contracts/test/verifiers/VerifierPlonk.sol @@ -19,1003 +19,970 @@ pragma solidity ^0.8.19; contract PlonkVerifier { - - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 14184380469316848647399916994526908634873673556047703492928820707587405857357; - uint256 private constant g2_srs_1_x_1 = 482275098823665719974591155934619915930048247564089153862907740325982636821; - uint256 private constant g2_srs_1_y_0 = 9081740555712312681676101658142837412124700599863904881497360479980542967997; - uint256 private constant g2_srs_1_y_1 = 10788505562695957146517509586265372291431743735546853483226752798469396098331; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 8; - uint256 private constant vk_inv_domain_size = 19152212512859365819465605027100115702479818850364030050735928663253832433665; - uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; - uint256 private constant vk_ql_com_x = 21146737814662248939271101735192940543382366687977110484636304065631052234661; - uint256 private constant vk_ql_com_y = 17244895415666211266680412689420596204100592899763916548999226920673373295795; - uint256 private constant vk_qr_com_x = 12034157544991483751879070773917816837151424951659313593179465195961958636762; - uint256 private constant vk_qr_com_y = 21128964842635530241689801368183970178962786540029862999283336804588522584697; - uint256 private constant vk_qm_com_x = 0; - uint256 private constant vk_qm_com_y = 0; - uint256 private constant vk_qo_com_x = 9690145773039397511522502225033489318603819532491614216035810227753062202328; - uint256 private constant vk_qo_com_y = 13758061819365328413779269411839899429431070095046925263255802702279536261884; - uint256 private constant vk_qk_com_x = 0; - uint256 private constant vk_qk_com_y = 0; - - uint256 private constant vk_s1_com_x = 17182197487722945203768515568014680528614078471464176366870022784845400646403; - uint256 private constant vk_s1_com_y = 20906636482132349356265024019355776966766221689385434048938253293206541987941; - - uint256 private constant vk_s2_com_x = 2847633030445395915601715055583454986756161565327551832016327694440474004530; - uint256 private constant vk_s2_com_y = 19207431948149911445928003407925263528194340126876487591340310230428671229550; - - uint256 private constant vk_s3_com_x = 19086081295379119079520891096686420879294280039419717609102045282826926824183; - uint256 private constant vk_s3_com_y = 6844673266018017607568220197100655211356735831371287938772913139123642785933; - - uint256 private constant vk_coset_shift = 5; - - - - - - uint256 private constant vk_nb_commitments_commit_api = 0; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x300; - uint256 private constant proof_opening_at_zeta_omega_y = 0x320; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; - // -> 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 = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - - - 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_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) - mstore(add(mem, state_pi), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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) - } - - 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) - } - - 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) - } - - 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_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) - } - // end errors ------------------------------------------------- - - // Beginning checks ------------------------------------------------- - - // s number of public inputs, p pointer the public inputs - function check_inputs_size(s, p) { - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) - if iszero(eq(actual_proof_size, expected_proof_size)) { - error_proof_size() - } - } - - function check_proof_openings_size(aproof) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(calldataload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where 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 - // nb_pi, pi respectively number of public inputs and public inputs - 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, 0x67616d6d61) // "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) - - // public inputs - let _mPtr := add(mPtr, 0x220) - let size_pi_in_bytes := mul(nb_pi, 0x20) - calldatacopy(_mPtr, pi, size_pi_in_bytes) - _mPtr := add(_mPtr, size_pi_in_bytes) - - // wire commitment commit api - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) - calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) - _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) - - let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 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)) - } - - function derive_beta(gamma_not_reduced)->beta_not_reduced{ - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "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)) - } - - // alpha depends on the previous challenge (beta) and on 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) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- - - // public input (not comming from the commit api) contribution - // ins, n are the public inputs and number of public inputs respectively - 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) - } - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - 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) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - 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) - } - } - - - // END compute_pi ------------------------------------------------- - - // 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) - } - - // 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] - function batch_verify_multi_points(aproof) { - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - 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, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) - if eq(check_staticcall, 0) { - 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) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[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}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) - mstore(add(state, state_folded_claimed_values), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) - 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) - 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) - 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) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i := add(i, 1)} - { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - } - - // 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): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [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: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) - mstore(add(mPtr,0x180), vk_s1_com_x) - mstore(add(mPtr,0x1a0), vk_s1_com_y) - mstore(add(mPtr,0x1c0), vk_s2_com_x) - mstore(add(mPtr,0x1e0), vk_s2_com_y) - - let offset := 0x200 - - - mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) - - - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - 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 eq(check_staticcall, 0) { - 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := add( - aproof, - add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20)) - ) - for { - let i := 0 - } lt(i, vk_nb_commitments_commit_api) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, state_linearised_polynomial_x), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - // 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] - // 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)) - 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) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - 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) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod)) - mstore(s2,mulmod(calldataload(add(aproof, proof_quotient_polynomial_at_zeta)),mload(add(state, state_zeta_power_n_minus_one)),r_mod)) - - mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) - } - - // BEGINNING utils math functions ------------------------------------------------- - function point_add(dst, p, q, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) - 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(), 6, mPtr, 0x80, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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,dst,0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- [s]src - function point_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, dst, 0x40) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) - 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(),6,mPtr,0x80,dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + [s]src (Elliptic curve) - 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(), 6, mPtr, 0x80, dst, 0x40)) - if iszero(l_success) { - error_ec_op() - } - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul_calldata(dst, src, s) { - let tmp := mulmod(calldataload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - 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(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = + 14184380469316848647399916994526908634873673556047703492928820707587405857357; + uint256 private constant g2_srs_1_x_1 = 482275098823665719974591155934619915930048247564089153862907740325982636821; + uint256 private constant g2_srs_1_y_0 = 9081740555712312681676101658142837412124700599863904881497360479980542967997; + uint256 private constant g2_srs_1_y_1 = + 10788505562695957146517509586265372291431743735546853483226752798469396098331; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 8; + uint256 private constant vk_inv_domain_size = + 19152212512859365819465605027100115702479818850364030050735928663253832433665; + uint256 private constant vk_omega = 19540430494807482326159819597004422086093766032135589407132600596362845576832; + uint256 private constant vk_ql_com_x = 21146737814662248939271101735192940543382366687977110484636304065631052234661; + uint256 private constant vk_ql_com_y = 17244895415666211266680412689420596204100592899763916548999226920673373295795; + uint256 private constant vk_qr_com_x = 12034157544991483751879070773917816837151424951659313593179465195961958636762; + uint256 private constant vk_qr_com_y = 21128964842635530241689801368183970178962786540029862999283336804588522584697; + uint256 private constant vk_qm_com_x = 0; + uint256 private constant vk_qm_com_y = 0; + uint256 private constant vk_qo_com_x = 9690145773039397511522502225033489318603819532491614216035810227753062202328; + uint256 private constant vk_qo_com_y = 13758061819365328413779269411839899429431070095046925263255802702279536261884; + uint256 private constant vk_qk_com_x = 0; + uint256 private constant vk_qk_com_y = 0; + + uint256 private constant vk_s1_com_x = 17182197487722945203768515568014680528614078471464176366870022784845400646403; + uint256 private constant vk_s1_com_y = 20906636482132349356265024019355776966766221689385434048938253293206541987941; + + uint256 private constant vk_s2_com_x = 2847633030445395915601715055583454986756161565327551832016327694440474004530; + uint256 private constant vk_s2_com_y = 19207431948149911445928003407925263528194340126876487591340310230428671229550; + + uint256 private constant vk_s3_com_x = 19086081295379119079520891096686420879294280039419717609102045282826926824183; + uint256 private constant vk_s3_com_y = 6844673266018017607568220197100655211356735831371287938772913139123642785933; + + uint256 private constant vk_coset_shift = 5; + + uint256 private constant vk_nb_commitments_commit_api = 0; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x1e0; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x280; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2c0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x2e0; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x300; + uint256 private constant proof_opening_at_zeta_omega_y = 0x320; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x340; + // -> 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 = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + 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_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) + mstore(add(mem, state_pi), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) + } + + 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) + } + + 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) + } + + 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_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) + } + // end errors ------------------------------------------------- + + // Beginning checks ------------------------------------------------- + + // s number of public inputs, p pointer the public inputs + function check_inputs_size(s, p) { + let input_checks := 1 + for { let i } lt(i, s) { i := add(i, 1) } { + input_checks := and(input_checks, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { error_inputs_size() } + } + + function check_proof_size(actual_proof_size) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api, 0x60)) + if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } + } + + function check_proof_openings_size(aproof) { + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + openings_check := and(openings_check, lt(calldataload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { error_proof_openings_size() } + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where 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 + // nb_pi, pi respectively number of public inputs and public inputs + 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, 0x67616d6d61) // "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) + + // public inputs + let _mPtr := add(mPtr, 0x220) + let size_pi_in_bytes := mul(nb_pi, 0x20) + calldatacopy(_mPtr, pi, size_pi_in_bytes) + _mPtr := add(_mPtr, size_pi_in_bytes) + + // wire commitment commit api + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + let size_wire_commitments_commit_api_in_bytes := mul(vk_nb_commitments_commit_api, 0x40) + calldatacopy(_mPtr, _proof, size_wire_commitments_commit_api_in_bytes) + _mPtr := add(_mPtr, size_wire_commitments_commit_api_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) + + let size := add(0x2c5, mul(nb_pi, 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 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)) + } + + function derive_beta(gamma_not_reduced) -> beta_not_reduced { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "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)) + } + + // alpha depends on the previous challenge (beta) and on 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) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + calldatacopy(add(mPtr, 0x40), add(aproof, proof_grand_product_commitment_x), 0x40) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { error_verify() } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "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 ------------------------------------------------- + + // public input (not comming from the commit api) contribution + // ins, n are the public inputs and number of public inputs respectively + 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) + } + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + 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) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + 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) + } + } + + // END compute_pi ------------------------------------------------- + + // 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) + } + + // 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] + function batch_verify_multi_points(aproof) { + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + 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, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40) + if eq(check_staticcall, 0) { 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) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[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}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr, 0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr, 0x60))) + mstore( + add(state, state_folded_claimed_values), + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)) + ) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x80), acc_gamma, mPtrOffset) + fr_acc_mul_calldata( + add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0xc0), acc_gamma, mPtrOffset) + 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(add(state, state_folded_digests_x), add(mPtr, 0x100), acc_gamma, add(mPtr, offset)) + 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(add(state, state_folded_digests_x), add(mPtr, 0x140), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x180), acc_gamma, add(mPtr, offset)) + 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) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr, 0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul_calldata(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + } + + // 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): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [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: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr, 0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr, 0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr, 0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr, 0xa0), mload(add(state, state_linearised_polynomial_y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, proof_l_com_x), 0xc0) + mstore(add(mPtr, 0x180), vk_s1_com_x) + mstore(add(mPtr, 0x1a0), vk_s1_com_y) + mstore(add(mPtr, 0x1c0), vk_s2_com_x) + mstore(add(mPtr, 0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr, offset), calldataload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, proof_s2_at_zeta))) + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api, 3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + 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 eq(check_staticcall, 0) { 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 commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := + add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) + for { let i := 0 } lt(i, vk_nb_commitments_commit_api) { i := add(i, 1) } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, state_linearised_polynomial_x), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + // 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] + // 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)) + 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) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + 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 + ) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(calldataload(add(aproof, proof_s1_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, proof_s2_at_zeta)), mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore( + computed_quotient, + addmod( + calldataload(add(aproof, proof_linearised_polynomial_at_zeta)), + mload(add(state, state_pi)), + r_mod + ) + ) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore( + computed_quotient, + addmod( + mload(computed_quotient), sub(r_mod, mload(add(state, state_alpha_square_lagrange_0))), r_mod + ) + ) + mstore( + s2, + mulmod( + calldataload(add(aproof, proof_quotient_polynomial_at_zeta)), + mload(add(state, state_zeta_power_n_minus_one)), + r_mod + ) + ) + + mstore(add(state, state_success), eq(mload(computed_quotient), mload(s2))) + } + + // BEGINNING utils math functions ------------------------------------------------- + function point_add(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + function point_add_calldata(dst, p, q, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- [s]src + function point_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, dst, 0x40) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst, src, s, mPtr) { + let state := mload(0x40) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + [s]src (Elliptic curve) + 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(), 6, mPtr, 0x80, dst, 0x40)) + if iszero(l_success) { error_ec_op() } + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul_calldata(dst, src, s) { + let tmp := mulmod(calldataload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) + if eq(check_staticcall, 0) { error_verify() } + res := mload(mPtr) + } } - res := mload(mPtr) - } } - } } diff --git a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol index b24a598ef..61eb03f83 100644 --- a/contracts/test/verifiers/VerifierPlonkRangeCheck.sol +++ b/contracts/test/verifiers/VerifierPlonkRangeCheck.sol @@ -19,1318 +19,1270 @@ pragma solidity ^0.8.19; contract PlonkVerifier { + uint256 private constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + 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 = 7814172549830033238223606142675268636058600480250623615512765003821404917357; + uint256 private constant G2_SRS_1_X_1 = + 13645852243604263853237508664752124496968745818264599716927438924652849501499; + uint256 private constant G2_SRS_1_Y_0 = + 21307838360152462312711713684081168554005605915621702901401407170852500681590; + uint256 private constant G2_SRS_1_Y_1 = + 21572117749168060448329421516439686064665732218582150646334081055061164833449; + + uint256 private constant G1_SRS_X = 1; + uint256 private constant G1_SRS_Y = 2; + + // ----------------------- vk --------------------- + uint256 private constant VK_NB_PUBLIC_INPUTS = 3; + uint256 private constant VK_DOMAIN_SIZE = 64; + uint256 private constant VK_INV_DOMAIN_SIZE = + 21546239076966786546898805655487630165289796206659533807077919746160561487873; + uint256 private constant VK_OMEGA = 9088801421649573101014283686030284801466796108869023335878462724291607593530; + uint256 private constant VK_QL_COM_X = 10398584435073889797632122505093129513546316083460155042201669609474507761393; + uint256 private constant VK_QL_COM_Y = 11330777933755117102961799810839691375686334590014561389258521851457877230369; + uint256 private constant VK_QR_COM_X = 4890069554988477196605704578982437675387725852092351667744598097766173864387; + uint256 private constant VK_QR_COM_Y = 19349603681767160646759566177297921942938550747056136456536680869640643726362; + uint256 private constant VK_QM_COM_X = 15102395100711249652702189888971210884726661069253717232051424256238336296154; + uint256 private constant VK_QM_COM_Y = 8429507290274846664135226417545857237016221800833374220037179018375269797509; + uint256 private constant VK_QO_COM_X = 8308944175243810613431144809106337373933253400523298809621497139697227011081; + uint256 private constant VK_QO_COM_Y = 15017491915531595518283812274044246345396629679070431756435244459826623666365; + uint256 private constant VK_QK_COM_X = 3633008490673505938984875516723788205584619065077429967414171095484684355226; + uint256 private constant VK_QK_COM_Y = 5924043858531262811057741401548398852576813068998609832283300183984594490265; + + uint256 private constant VK_S1_COM_X = 1831950617253311161965086323296612321621431881366015012145754662717020653254; + uint256 private constant VK_S1_COM_Y = 12552778567595986457625193226638552613143112724414943417872328062638651381509; + + uint256 private constant VK_S2_COM_X = 9282332095318458810329272575154746196077473337194803812097690515423473875292; + uint256 private constant VK_S2_COM_Y = 4132295717165950971373345207419370482187113718476938372487185645861198998345; + + uint256 private constant VK_S3_COM_X = 5528050128912291205450410066466372880115605758861985145700038793768988564158; + uint256 private constant VK_S3_COM_Y = 5265831924970622321828286611341299461171200084467948717918107531865540077415; - uint256 private constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - 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 = 7814172549830033238223606142675268636058600480250623615512765003821404917357; - uint256 private constant G2_SRS_1_X_1 = 13645852243604263853237508664752124496968745818264599716927438924652849501499; - uint256 private constant G2_SRS_1_Y_0 = 21307838360152462312711713684081168554005605915621702901401407170852500681590; - uint256 private constant G2_SRS_1_Y_1 = 21572117749168060448329421516439686064665732218582150646334081055061164833449; - - uint256 private constant G1_SRS_X = 1; - uint256 private constant G1_SRS_Y = 2; - - // ----------------------- vk --------------------- - uint256 private constant VK_NB_PUBLIC_INPUTS = 3; - uint256 private constant VK_DOMAIN_SIZE = 64; - uint256 private constant VK_INV_DOMAIN_SIZE = 21546239076966786546898805655487630165289796206659533807077919746160561487873; - uint256 private constant VK_OMEGA = 9088801421649573101014283686030284801466796108869023335878462724291607593530; - uint256 private constant VK_QL_COM_X = 10398584435073889797632122505093129513546316083460155042201669609474507761393; - uint256 private constant VK_QL_COM_Y = 11330777933755117102961799810839691375686334590014561389258521851457877230369; - uint256 private constant VK_QR_COM_X = 4890069554988477196605704578982437675387725852092351667744598097766173864387; - uint256 private constant VK_QR_COM_Y = 19349603681767160646759566177297921942938550747056136456536680869640643726362; - uint256 private constant VK_QM_COM_X = 15102395100711249652702189888971210884726661069253717232051424256238336296154; - uint256 private constant VK_QM_COM_Y = 8429507290274846664135226417545857237016221800833374220037179018375269797509; - uint256 private constant VK_QO_COM_X = 8308944175243810613431144809106337373933253400523298809621497139697227011081; - uint256 private constant VK_QO_COM_Y = 15017491915531595518283812274044246345396629679070431756435244459826623666365; - uint256 private constant VK_QK_COM_X = 3633008490673505938984875516723788205584619065077429967414171095484684355226; - uint256 private constant VK_QK_COM_Y = 5924043858531262811057741401548398852576813068998609832283300183984594490265; - - uint256 private constant VK_S1_COM_X = 1831950617253311161965086323296612321621431881366015012145754662717020653254; - uint256 private constant VK_S1_COM_Y = 12552778567595986457625193226638552613143112724414943417872328062638651381509; - - uint256 private constant VK_S2_COM_X = 9282332095318458810329272575154746196077473337194803812097690515423473875292; - uint256 private constant VK_S2_COM_Y = 4132295717165950971373345207419370482187113718476938372487185645861198998345; - - uint256 private constant VK_S3_COM_X = 5528050128912291205450410066466372880115605758861985145700038793768988564158; - uint256 private constant VK_S3_COM_Y = 5265831924970622321828286611341299461171200084467948717918107531865540077415; - - uint256 private constant VK_COSET_SHIFT = 5; - - - uint256 private constant VK_QCP_0_X = 21793598771508025947887671914700122097813101646208783481283278104736097325441; - uint256 private constant VK_QCP_0_Y = 19261568386046758090660517995286593584435610364418601346457053212061975687679; - - - uint256 private constant VK_INDEX_COMMIT_API0 = 14; - uint256 private constant VK_NB_CUSTOM_GATES = 1; - - // ------------------------------------------------ - - // offset proof - uint256 private constant PROOF_L_COM_X = 0x00; - 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; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta) - uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - 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) - uint256 private constant PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA = 0x280; // t(zeta) - uint256 private constant PROOF_LINEARISED_POLYNOMIAL_AT_ZETA = 0x2a0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x2c0; // [Wzeta] - uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2e0; - - uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x300; - uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x320; - - uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x340; - uint256 private constant PROOF_COMMITMENTS_WIRES_CUSTOM_GATES = 0x360; - - // -> 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 = 0x00; - uint256 private constant STATE_BETA = 0x20; - uint256 private constant STATE_GAMMA = 0x40; - uint256 private constant STATE_ZETA = 0x60; - - // reusable value - uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80; - - // commitment to H - uint256 private constant STATE_FOLDED_H_X = 0xa0; - uint256 private constant STATE_FOLDED_H_Y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0; - uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant STATE_FOLDED_DIGESTS_X = 0x140; - uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x160; - - uint256 private constant STATE_PI = 0x180; - - uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1a0; - - uint256 private constant STATE_GAMMA_KZG = 0x1c0; - - uint256 private constant STATE_SUCCESS = 0x1e0; - uint256 private constant STATE_CHECK_VAR = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant STATE_LAST_MEM = 0x220; - - // -------- 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; - - - /// 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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) - l_pi := addmod(l_wocommit, l_pi, R_MOD) - mstore(add(mem, STATE_PI), l_pi) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(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_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() + uint256 private constant VK_COSET_SHIFT = 5; + + uint256 private constant VK_QCP_0_X = 21793598771508025947887671914700122097813101646208783481283278104736097325441; + uint256 private constant VK_QCP_0_Y = 19261568386046758090660517995286593584435610364418601346457053212061975687679; + + uint256 private constant VK_INDEX_COMMIT_API0 = 14; + uint256 private constant VK_NB_CUSTOM_GATES = 1; + + // ------------------------------------------------ + + // offset proof + uint256 private constant PROOF_L_COM_X = 0x00; + 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; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta) + uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + 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) + uint256 private constant PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA = 0x280; // t(zeta) + uint256 private constant PROOF_LINEARISED_POLYNOMIAL_AT_ZETA = 0x2a0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x2c0; // [Wzeta] + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2e0; + + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x300; + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x320; + + uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x340; + uint256 private constant PROOF_COMMITMENTS_WIRES_CUSTOM_GATES = 0x360; + + // -> 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 = 0x00; + uint256 private constant STATE_BETA = 0x20; + uint256 private constant STATE_GAMMA = 0x40; + uint256 private constant STATE_ZETA = 0x60; + + // reusable value + uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80; + + // commitment to H + uint256 private constant STATE_FOLDED_H_X = 0xa0; + uint256 private constant STATE_FOLDED_H_Y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0; + uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_X = 0x140; + uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x160; + + uint256 private constant STATE_PI = 0x180; + + uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1a0; + + uint256 private constant STATE_GAMMA_KZG = 0x1c0; + + uint256 private constant STATE_SUCCESS = 0x1e0; + uint256 private constant STATE_CHECK_VAR = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant STATE_LAST_MEM = 0x220; + + // -------- 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; + + /// 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_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_wocommit, l_pi, R_MOD) + mstore(add(mem, STATE_PI), l_pi) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(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_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) { + let input_checks := 1 + for { let i } lt(i, s) { i := add(i, 1) } { + input_checks := and(input_checks, lt(calldataload(p), R_MOD)) + p := add(p, 0x20) + } + if iszero(input_checks) { error_inputs_size() } + } + + /// 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(0x340, 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) { + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // quotient polynomial at zeta + p := add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_L_AT_ZETA + p := add(aproof, PROOF_L_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_R_AT_ZETA + p := add(aproof, PROOF_R_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_O_AT_ZETA + p := add(aproof, PROOF_O_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_S1_AT_ZETA + p := add(aproof, PROOF_S1_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_S2_AT_ZETA + p := add(aproof, PROOF_S2_AT_ZETA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA + p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA) + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + + // 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) } { + openings_check := and(openings_check, lt(calldataload(p), R_MOD)) + p := add(p, 0x20) + } + + if iszero(openings_check) { error_proof_openings_size() } + } + // 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, 0x67616d6d61) // "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, 0x62657461) // "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, 0x616C706861) // "alpha" + let _mPtr := add(mPtr, 0x20) + mstore(_mPtr, beta_not_reduced) + _mPtr := add(_mPtr, 0x20) + + // Bsb22Commitments + let proof_bsb_commitments := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + 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, 0x7a657461) // "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_COMMITMENTS_WIRES_CUSTOM_GATES) + + 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_API0), 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) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, STATE_SUCCESS)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, STATE_SUCCESS), res_pairing) + } + + /// @notice Fold the opening proofs at ζ: + /// * at state+state_folded_digest we store: [H] + γ[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_FOLDED_H_X))) + mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_FOLDED_H_Y))) + mstore( + add(state, STATE_FOLDED_CLAIMED_VALUES), + calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)) + ) + + point_acc_mul(state_folded_digests, add(state, STATE_LINEARISED_POLYNOMIAL_X), acc_gamma, mPtr) + fr_acc_mul_calldata( + add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA), acc_gamma + ) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) + 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 poscaz := 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), poscaz, acc_gamma) + poscaz := add(poscaz, 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): + /// * ζ + /// * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + /// * [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: + /// * H(ζ) + /// * 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, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA))) + mstore(add(mPtr, 0x40), mload(add(state, STATE_FOLDED_H_X))) + mstore(add(mPtr, 0x60), mload(add(state, STATE_FOLDED_H_Y))) + mstore(add(mPtr, 0x80), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) + mstore(add(mPtr, 0xa0), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) + calldatacopy(add(mPtr, 0xc0), add(aproof, PROOF_L_COM_X), 0xc0) + mstore(add(mPtr, 0x180), VK_S1_COM_X) + mstore(add(mPtr, 0x1a0), VK_S1_COM_Y) + mstore(add(mPtr, 0x1c0), VK_S2_COM_X) + mstore(add(mPtr, 0x1e0), VK_S2_COM_Y) + + let offset := 0x200 + + 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), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_L_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_R_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_O_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) + + let _mPtr := add(mPtr, add(offset, 0xe0)) + + let _poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + for { let i := 0 } lt(i, VK_NB_CUSTOM_GATES) { i := add(i, 1) } { + mstore(_mPtr, calldataload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x17, mul(VK_NB_CUSTOM_GATES, 3)) // number of 32bytes elmts = 0x17 (zeta+2*7+7 for the digests+openings) + 2*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + VK_NB_CUSTOM_GATES (for the openings of the selectors) + 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 commits_api_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + let commits_api := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + for { let i := 0 } lt(i, VK_NB_CUSTOM_GATES) { i := add(i, 1) } { + mstore(mPtr, calldataload(commits_api)) + mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + point_acc_mul( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + mPtr, + calldataload(commits_api_at_zeta), + add(mPtr, 0x40) + ) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 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)) + } + + /// @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] + /// 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)) + /// @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 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 + ) + } + + /// @notice check that + /// L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + /// α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + /// + α²*L₁(ζ) = + /// (ζⁿ-1)H(ζ) + /// @param aproof pointer to the proof + function verify_quotient_poly_eval_at_zeta(aproof) { + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), STATE_LAST_MEM) + mstore(s1, mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) + mstore(s1, addmod(mload(s1), mload(add(state, STATE_GAMMA)), R_MOD)) + mstore(s1, addmod(mload(s1), calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1, 0x20) + mstore(s2, mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) + mstore(s2, addmod(mload(s2), mload(add(state, STATE_GAMMA)), R_MOD)) + mstore(s2, addmod(mload(s2), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1, 0x40) + mstore(o, addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), R_MOD)) + mstore(s1, mulmod(mload(s1), mload(o), R_MOD)) + mstore(s1, mulmod(mload(s1), mload(add(state, STATE_ALPHA)), R_MOD)) + mstore(s1, mulmod(mload(s1), calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD)) + + let computed_quotient := add(s1, 0x60) + + // linearizedpolynomial + pi(zeta) + mstore( + computed_quotient, + addmod( + calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)), + mload(add(state, STATE_PI)), + R_MOD + ) + ) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), R_MOD)) + mstore( + computed_quotient, + addmod( + mload(computed_quotient), sub(R_MOD, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))), R_MOD + ) + ) + mstore( + s2, + mulmod( + calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)), + mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)), + R_MOD + ) + ) + + mstore(add(state, STATE_SUCCESS), eq(mload(computed_quotient), mload(s2))) + } + + // 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) { + let state := mload(0x40) + 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(), 6, 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) { + let state := mload(0x40) + 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(), 6, 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) { + let state := mload(0x40) + 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, 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) { + 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, 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) { + let state := mload(0x40) + 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(), 6, 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(), 6, 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(), 0x05, mPtr, 0xc0, mPtr, 0x20) + if eq(check_staticcall, 0) { error_verify() } + res := mload(mPtr) + } } - } - - /// 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) { - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(calldataload(p), R_MOD)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - /// 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(0x340, 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) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // quotient polynomial at zeta - p := add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_L_AT_ZETA - p := add(aproof, PROOF_L_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_R_AT_ZETA - p := add(aproof, PROOF_R_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_O_AT_ZETA - p := add(aproof, PROOF_O_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_S1_AT_ZETA - p := add(aproof, PROOF_S1_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_S2_AT_ZETA - p := add(aproof, PROOF_S2_AT_ZETA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA - p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA) - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - - // 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)} - { - openings_check := and(openings_check, lt(calldataload(p), R_MOD)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // 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, 0x67616d6d61) // "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, 0x62657461) // "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, 0x616C706861) // "alpha" - let _mPtr := add(mPtr, 0x20) - mstore(_mPtr, beta_not_reduced) - _mPtr := add(_mPtr, 0x20) - - // Bsb22Commitments - let proof_bsb_commitments := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) - 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, 0x7a657461) // "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_COMMITMENTS_WIRES_CUSTOM_GATES) - - 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_API0), 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) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, STATE_SUCCESS)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, STATE_SUCCESS), res_pairing) - } - - /// @notice Fold the opening proofs at ζ: - /// * at state+state_folded_digest we store: [H] + γ[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_FOLDED_H_X))) - mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_FOLDED_H_Y))) - mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) - - point_acc_mul(state_folded_digests, add(state, STATE_LINEARISED_POLYNOMIAL_X), acc_gamma, mPtr) - fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) - 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 poscaz := 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), poscaz, acc_gamma) - poscaz := add(poscaz, 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): - /// * ζ - /// * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - /// * [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: - /// * H(ζ) - /// * 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, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA))) - mstore(add(mPtr,0x40), mload(add(state, STATE_FOLDED_H_X))) - mstore(add(mPtr,0x60), mload(add(state, STATE_FOLDED_H_Y))) - mstore(add(mPtr,0x80), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) - mstore(add(mPtr,0xa0), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, PROOF_L_COM_X), 0xc0) - mstore(add(mPtr,0x180), VK_S1_COM_X) - mstore(add(mPtr,0x1a0), VK_S1_COM_Y) - mstore(add(mPtr,0x1c0), VK_S2_COM_X) - mstore(add(mPtr,0x1e0), VK_S2_COM_Y) - - let offset := 0x200 - - 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), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_L_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_R_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_O_AT_ZETA))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) - - let _mPtr := add(mPtr, add(offset, 0xe0)) - - let _poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) - for {let i:=0} lt(i, VK_NB_CUSTOM_GATES) {i:=add(i,1)} - { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - - mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x17, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x17 (zeta+2*7+7 for the digests+openings) + 2*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + VK_NB_CUSTOM_GATES (for the openings of the selectors) - 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 commits_api_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) - let commits_api := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) - for { - let i := 0 - } lt(i, VK_NB_CUSTOM_GATES) { - i := add(i, 1) - } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) - point_acc_mul( - add(state, STATE_LINEARISED_POLYNOMIAL_X), - mPtr, - calldataload(commits_api_at_zeta), - add(mPtr, 0x40) - ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 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)) - } - - /// @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] - /// 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)) - /// @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 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) - } - - /// @notice check that - /// L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - /// α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - /// + α²*L₁(ζ) = - /// (ζⁿ-1)H(ζ) - /// @param aproof pointer to the proof - function verify_quotient_poly_eval_at_zeta(aproof) { - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), STATE_LAST_MEM) - mstore(s1, mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) - mstore(s1, addmod(mload(s1), mload(add(state, STATE_GAMMA)), R_MOD)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) - mstore(s2, addmod(mload(s2), mload(add(state, STATE_GAMMA)), R_MOD)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), R_MOD)) - mstore(s1, mulmod(mload(s1), mload(o), R_MOD)) - mstore(s1, mulmod(mload(s1), mload(add(state, STATE_ALPHA)), R_MOD)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)), mload(add(state, STATE_PI)), R_MOD)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), R_MOD)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(R_MOD, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))), R_MOD)) - mstore(s2,mulmod(calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)),mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)),R_MOD)) - - mstore(add(state, STATE_SUCCESS), eq(mload(computed_quotient), mload(s2))) - } - - // 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) { - let state := mload(0x40) - 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(),6,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) { - let state := mload(0x40) - 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(), 6, 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) { - let state := mload(0x40) - 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,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) { - 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, 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) { - let state := mload(0x40) - 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(),6,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(), 6, 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(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() - } - res := mload(mPtr) - } } - } } From 41bb8bd3822eeac50a9a310e654ae58795f52a90 Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Wed, 18 Oct 2023 00:59:00 -0700 Subject: [PATCH 28/30] clippy --- plonky2x/core/src/backend/function/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index a8d7024da..b145db0ad 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -49,7 +49,7 @@ pub trait Plonky2xFunction { fn entrypoint(); /// Returns the verifier contract for the circuit. - fn verifier(circuit_digest: &str, wrapper_path: &String) -> String; + fn verifier(circuit_digest: &str, wrapper_path: &str) -> String; } impl Plonky2xFunction for C { @@ -138,7 +138,7 @@ impl Plonky2xFunction for C { AlgebraicHasher, OuterParameters::Config: Serialize, { - let gnark_wrapper_process = if args.wrapper_path != "" { + let gnark_wrapper_process = if !args.wrapper_path.is_empty() { // If the wrapper path is provided, then we know we will be wrapping the proof let child_process = std::process::Command::new(path::Path::new(&args.wrapper_path).join("verifier")) @@ -276,7 +276,7 @@ impl Plonky2xFunction for C { } } - fn verifier(circuit_digest: &str, wrapper_path: &String) -> String { + fn verifier(circuit_digest: &str, wrapper_path: &str) -> String { let wrapper_verifier_path = format!("{}/Verifier.sol", wrapper_path); let wrapper_verifier_contract = fs::read_to_string(wrapper_verifier_path) .expect("Failed to read wrapper_verifier_path"); From 93ebac553048c4b791c9786384540ba07f1d6c82 Mon Sep 17 00:00:00 2001 From: John Guibas Date: Wed, 18 Oct 2023 10:40:08 -0700 Subject: [PATCH 29/30] fix: john edits --- .gitignore | 7 +++- .../{TestVerifier.t.sol => Verifiers.t.sol} | 6 +-- .../{ => fixtures}/groth16_proof_data.json | 0 .../{ => fixtures}/plonk_proof_data.json | 0 .../plonk_proof_data_range_check.json | 0 plonky2x/core/examples/evm.rs | 14 +------ plonky2x/core/src/backend/function/mod.rs | 38 ++++++++++--------- plonky2x/core/src/backend/wrapper/wrap.rs | 23 ++++++----- 8 files changed, 41 insertions(+), 47 deletions(-) rename contracts/test/verifiers/{TestVerifier.t.sol => Verifiers.t.sol} (91%) rename contracts/test/verifiers/{ => fixtures}/groth16_proof_data.json (100%) rename contracts/test/verifiers/{ => fixtures}/plonk_proof_data.json (100%) rename contracts/test/verifiers/{ => fixtures}/plonk_proof_data_range_check.json (100%) diff --git a/.gitignore b/.gitignore index bd8b2b954..9c3562143 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,9 @@ keystore .idea/ proof.json -output.json \ No newline at end of file +output.json + +**/verifier-build +**/verifier-build-groth16 +plonky2x/core/wrapped +*.tar.gz \ No newline at end of file diff --git a/contracts/test/verifiers/TestVerifier.t.sol b/contracts/test/verifiers/Verifiers.t.sol similarity index 91% rename from contracts/test/verifiers/TestVerifier.t.sol rename to contracts/test/verifiers/Verifiers.t.sol index 2c9100d39..6beec326a 100644 --- a/contracts/test/verifiers/TestVerifier.t.sol +++ b/contracts/test/verifiers/Verifiers.t.sol @@ -15,7 +15,7 @@ contract VerifierTest is Test { function testVerifierGroth16() public { Groth16Verifier verifier = new Groth16Verifier(); - string memory groth16Json = vm.readFile("test/verifiers/groth16_proof_data.json"); + string memory groth16Json = vm.readFile("test/verifiers/fixtures/groth16_proof_data.json"); uint256[] memory proof = stdJson.readUintArray(groth16Json, "$.proof"); uint256[] memory input = stdJson.readUintArray(groth16Json, "$.inputs"); @@ -43,7 +43,7 @@ contract VerifierTest is Test { function testVerifierPlonk() public { PlonkVerifier verifier = new PlonkVerifier(); - string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data.json"); + string memory proofJson = vm.readFile("test/verifiers/fixtures/plonk_proof_data.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); @@ -54,7 +54,7 @@ contract VerifierTest is Test { function testVerifierPlonkRangeCheck() public { PlonkRangeCheckVerifier verifier = new PlonkRangeCheckVerifier(); - string memory proofJson = vm.readFile("test/verifiers/plonk_proof_data_range_check.json"); + string memory proofJson = vm.readFile("test/verifiers/fixtures/plonk_proof_data_range_check.json"); bytes memory proof = stdJson.readBytes(proofJson, "$.proof"); uint256[] memory input = stdJson.readUintArray(proofJson, "$.inputs"); uint256 startGas = gasleft(); diff --git a/contracts/test/verifiers/groth16_proof_data.json b/contracts/test/verifiers/fixtures/groth16_proof_data.json similarity index 100% rename from contracts/test/verifiers/groth16_proof_data.json rename to contracts/test/verifiers/fixtures/groth16_proof_data.json diff --git a/contracts/test/verifiers/plonk_proof_data.json b/contracts/test/verifiers/fixtures/plonk_proof_data.json similarity index 100% rename from contracts/test/verifiers/plonk_proof_data.json rename to contracts/test/verifiers/fixtures/plonk_proof_data.json diff --git a/contracts/test/verifiers/plonk_proof_data_range_check.json b/contracts/test/verifiers/fixtures/plonk_proof_data_range_check.json similarity index 100% rename from contracts/test/verifiers/plonk_proof_data_range_check.json rename to contracts/test/verifiers/fixtures/plonk_proof_data_range_check.json diff --git a/plonky2x/core/examples/evm.rs b/plonky2x/core/examples/evm.rs index dcc7c0ca6..56b2aef89 100644 --- a/plonky2x/core/examples/evm.rs +++ b/plonky2x/core/examples/evm.rs @@ -21,7 +21,6 @@ use plonky2x::backend::circuit::{Circuit, PlonkParameters}; use plonky2x::backend::function::Plonky2xFunction; use plonky2x::frontend::vars::ByteVariable; use plonky2x::prelude::CircuitBuilder; -use plonky2x::utils; #[derive(Debug, Clone)] struct SimpleAdditionCircuit; @@ -36,7 +35,6 @@ impl Circuit for SimpleAdditionCircuit { } fn main() { - utils::setup_logger(); // So that we can see `builder.watch` during proof generation time SimpleAdditionCircuit::entrypoint(); } @@ -46,7 +44,7 @@ mod tests { use plonky2x::backend::circuit::config::{DefaultParameters, Groth16WrapperParameters}; use plonky2x::backend::wrapper::wrap::WrappedCircuit; - use plonky2x::prelude::{GoldilocksField, PoseidonGoldilocksConfig}; + use plonky2x::prelude::PoseidonGoldilocksConfig; use super::*; @@ -67,17 +65,9 @@ mod tests { assert_eq!(xor, 1u8); } - #[test] - fn test_circuit_function_evm_input_json() { - // let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // let path = format!("{}/examples/evm.json", root.display()); - // Function::test::(path); - } - #[test] fn test_evm_wrap() { utils::setup_logger(); - let mut builder = CircuitBuilder::::new(); SimpleAdditionCircuit::define(&mut builder); let circuit = builder.build(); @@ -91,7 +81,5 @@ mod tests { let wrapper: WrappedCircuit<_, _, 2> = WrappedCircuit::::build(circuit); let wrapped_proof = wrapper.prove(&proof).unwrap(); - // dummy_wrapped_proof.save(dummy_path).unwrap(); - println!("Saved dummy_circuit"); } } diff --git a/plonky2x/core/src/backend/function/mod.rs b/plonky2x/core/src/backend/function/mod.rs index b145db0ad..22c652d4b 100644 --- a/plonky2x/core/src/backend/function/mod.rs +++ b/plonky2x/core/src/backend/function/mod.rs @@ -87,29 +87,31 @@ impl Plonky2xFunction for C { let contract_path = format!("{}/FunctionVerifier.sol", args.build_dir); let mut contract_file = File::create(&contract_path).unwrap(); + // The wrapper circuit digest will get saved in the Solidity smart contract, which will + // use this value as a public input `VerifierDigest` in the gnark plonky2 verifier. info!("First building wrapper circuit to get the wrapper circuit digest..."); - // The wrapper circuit digest will get saved in the Solidity smart contract, - // which will use this value as a public input `VerifierDigest` in the gnark plonky2 verifier let wrapped_circuit = WrappedCircuit::::build(circuit); + // to_bytes() returns the representation as LE, but we want to save it on-chain as BE + // because that is the format of the public input to the gnark plonky2 verifier. let mut circuit_digest_bytes = wrapped_circuit .wrapper_circuit .data .verifier_only .circuit_digest .to_bytes(); - // to_bytes() returns the representation as LE, but we want to save it on-chain - // as BE because that is the format of the public input to the gnark plonky2 verifier. circuit_digest_bytes.reverse(); - // The VerifierDigest is stored on-chain as a bytes32, so we need to pad it with 0s - // to store it in the Solidity smart contract. - // Note that we don't need to do any sort of truncation of the top bits because the - // circuit digest already lives in the bn254 field because the WrappedCircuit config - // is the Poseidon bn254 hasher. - // In fact in the Solidity smart contract we should *not* truncate the top 3 bits - // like we do with input_hash and output_hash, as the circuit digest has a - // small probability of being greater than 2^253, as the field modulus is 254 bits. + // The VerifierDigest is stored onchain as a bytes32, so we need to pad it with 0s + // to store it in the solidity smart contract. + // + // Note that we don't need to do any sort of truncation of the most significant bits + // because the circuit digest already lives in the bn254 field because the prover config + // uses the Poseidon bn254 hasher. + // + // In the solidity smart contract we should not truncate the 3 most significant bits + // like we do with input_hash and output_hash as the circuit digest has a small + // probability of being greater than 2^253 given that the field modulus is 254 bits. let mut padded = vec![0u8; 32]; let digest_len = circuit_digest_bytes.len(); padded[(32 - digest_len)..].copy_from_slice(&circuit_digest_bytes); @@ -194,9 +196,9 @@ impl Plonky2xFunction for C { } if let PublicOutput::Bytes(output_bytes) = output { - info!("Output Bytes: 0x{}", hex::encode(output_bytes.clone())); // It's quite fast (~5-10 seconds) to rebuild the wrapped circuit. Because of this we // choose to rebuild here instead of loading from disk. + info!("Output Bytes: 0x{}", hex::encode(output_bytes.clone())); let wrapped_circuit = WrappedCircuit::::build(circuit); let wrapped_proof = wrapped_circuit.prove(&proof).expect("failed to wrap proof"); @@ -204,9 +206,8 @@ impl Plonky2xFunction for C { .save("wrapped") .expect("failed to save wrapped proof"); - // The gnark_wrapper_process should have been started + // The gnark_wrapper_process should have been started. let mut gnark_wrapper_process = gnark_wrapper_process.unwrap(); - let mut stdin_opt = None; while stdin_opt.is_none() { stdin_opt = match gnark_wrapper_process.stdin.as_mut() { @@ -221,25 +222,26 @@ impl Plonky2xFunction for C { } }; } - let stdin = stdin_opt.unwrap(); + let stdin = stdin_opt.unwrap(); stdin .write_all(b"wrapped\n") .expect("Failed to write to stdin"); let verifier_output = gnark_wrapper_process .wait_with_output() .expect("failed to execute process"); + if !verifier_output.status.success() { panic!("verifier failed"); } - // Read result from gnark verifier + // Read result from gnark verifier. let file = std::fs::File::open("proof.json").unwrap(); let rdr = std::io::BufReader::new(file); let result_data = serde_json::from_reader::, BytesResultData>(rdr).unwrap(); - // Write full result with output bytes to output.json + // Write full result with output bytes to output.json. let result: ProofResult = ProofResult::from_bytes(result_data.proof, output_bytes); let json = serde_json::to_string_pretty(&result).unwrap(); diff --git a/plonky2x/core/src/backend/wrapper/wrap.rs b/plonky2x/core/src/backend/wrapper/wrap.rs index 30f2de13a..814ecfa35 100644 --- a/plonky2x/core/src/backend/wrapper/wrap.rs +++ b/plonky2x/core/src/backend/wrapper/wrap.rs @@ -43,7 +43,7 @@ where >::Hasher: AlgebraicHasher, { pub fn build(circuit: CircuitBuild) -> Self { - // Standartize the public inputs/outputs to their hash and verify the circuit recursively + // Standartize the public inputs/outputs to their hash and verify the circuit recursively. let mut hash_builder = CircuitBuilder::::new(); let circuit_proof_target = hash_builder.add_virtual_proof_with_pis(&circuit.data.common); let circuit_verifier_target = @@ -78,8 +78,8 @@ where hash_builder.watch(&output_hash, "output_hash"); // We must truncate the top 3 bits because in the gnark-plonky2-verifier, the input_hash - // and output_hash are both represented as 1 field element in the BN254 field - // to reduce on-chain verification costs. + // and output_hash are both represented as 1 field element in the BN254 field to reduce + // onchain verification costs. let input_hash_zeroed = hash_builder.mask_be_bits(input_hash, 3); let output_hash_zeroed = hash_builder.mask_be_bits(output_hash, 3); @@ -101,9 +101,9 @@ where hash_builder.watch_slice(&input_vars, "input_hash_truncated as vars"); hash_builder.watch_slice(&output_vars, "output_hash_truncated as vars"); - // Write input_hash, output_hash to public_inputs - // In the gnark-plonky2-verifier, these 64 bytes get summed to 2 field elements that - // correspond to the input_hash and output_hash respectively as public inputs. + // Write input_hash, output_hash to public_inputs. In the gnark-plonky2-verifier, these + // 64 bytes get summed to 2 field elements that correspond to the input_hash and output_hash + // respectively as public inputs. input_vars .clone() .into_iter() @@ -113,7 +113,7 @@ where }); let hash_circuit = hash_builder.build(); - // An inner recursion to standardize the degree + // An inner recursion to standardize the degree. let mut recursive_builder = CircuitBuilder::::new(); let hash_proof_target = recursive_builder.add_virtual_proof_with_pis(&hash_circuit.data.common); @@ -124,7 +124,6 @@ where &hash_verifier_target, &hash_circuit.data.common, ); - assert_eq!(hash_proof_target.public_inputs.len(), 32usize * 2); recursive_builder @@ -137,7 +136,7 @@ where recursive_circuit.data.common.degree() ); - // Finally, wrap this in the outer circuit + // Finally, wrap this in the outer circuit. let mut wrapper_builder = CircuitBuilder::::new(); let proof_target = wrapper_builder.add_virtual_proof_with_pis(&recursive_circuit.data.common); @@ -290,14 +289,14 @@ mod tests { let path = format!("{}/test_circuit/", build_path); let dummy_path = format!("{}/dummy/", build_path); - // Create an inner circuit for verification + // Create an inner circuit for verification. let mut builder = CircuitBuilder::::new(); let a = builder.evm_read::(); let b = builder.evm_read::(); let c = builder.xor(a, b); builder.evm_write(c); - // Set up the dummy circuit and wrapper + // Set up the dummy circuit and wrapper. let dummy_circuit = builder.build(); let mut dummy_input = dummy_circuit.input(); dummy_input.evm_write::(0u8); @@ -312,7 +311,7 @@ mod tests { dummy_wrapped_proof.save(dummy_path).unwrap(); println!("Saved dummy_circuit"); - // Set up the circuit and wrapper + // Set up the circuit and wrapper. let msg = b"plonky2"; let msg_bits = to_bits(msg.to_vec()); let expected_digest = "8943a85083f16e93dc92d6af455841daacdae5081aa3125b614a626df15461eb"; From 255f09b4fc4fe98c02856692ce14951026e173ab Mon Sep 17 00:00:00 2001 From: John Guibas Date: Wed, 18 Oct 2023 10:40:24 -0700 Subject: [PATCH 30/30] fix: john edits --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9c3562143..9a0dfd88c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ keystore **/__pycache__ **/FunctionVerifier.sol +**/broadcast .idea/ proof.json