diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc0ba30a..3577d3ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,81 @@ and this project adheres to Rust's notion of ## [Unreleased] +### Added +- `orchard::note::AssetBase`, the ZSA note type identifier: + - `AssetBase::zatoshi`, the asset used by every Orchard note built through this crate's + public APIs today. + - `AssetBase::{from_bytes, to_bytes}`, (de)serializing an `AssetBase`. + - `AssetBase::is_zatoshi`, testing whether it is the zatoshi asset. + - `AssetBase::cv_base`, the base point used to derive a value commitment for notes of this + asset. +- `orchard::Note::asset`, returning a note's `AssetBase` (currently always + `AssetBase::zatoshi()`). +- `impl subtle::ConditionallySelectable for orchard::note::RandomSeed`. +- `orchard::bundle::Flags::zsa_enabled`, the accessor for the new ZSA flag bit. No public + constructor can currently set it to `true`. +- `orchard::circuit_version` module, containing `OrchardCircuitVersion` (moved out of + `orchard::circuit`; see Changed) with a new `OrchardCircuitVersion::ZSA` variant selecting + the ZSA Action circuit. +- `orchard::value::ValueCommitment::derive_with_asset`, deriving a value commitment for an + arbitrary `AssetBase`. +- `unstable-voting-circuits`-only (not covered by the crate's semver guarantees): + - Constants backing the new ZSA note commitment domain and split-note nullifier derivation: + - `orchard::constants::fixed_bases::NOTE_ZSA_COMMITMENT_PERSONALIZATION` + - `orchard::constants::sinsemilla::Q_NOTE_ZSA_COMMITMENT_M_GENERATOR` + - `orchard::constants::sinsemilla::{OrchardHashDomains::NoteZsaCommit, + OrchardCommitDomains::NoteZsaCommit}` variants + - `orchard::constants::nullifier_l` module, with the `NULLIFIER_L` constant used to derive + a split note's nullifier + - The types carrying the ZSA-specific inputs that the gadgets now take (see Changed): + - `orchard::circuit::derive_nullifier::ZsaNullifierParams` + - `orchard::circuit::note_commit::ZsaNoteCommitParams` + +### Changed +- The following already-opaque public structs gained new private fields for future ZSA + support (none has a public accessor beyond `Note::asset`, listed under Added): + - `orchard::note::Note`: `asset`, `rseed_split_note` + - `orchard::builder::SpendInfo`: `split_flag` + - `orchard::builder::OutputInfo`: `asset` + - `orchard::circuit::Instance`: `enable_zsa` +- `orchard::Proof::expected_proof_size` now takes an `OrchardCircuitVersion` argument, since + the ZSA circuit's proof size differs from the other circuit versions'. +- `OrchardCircuitVersion` moved from `orchard::circuit::OrchardCircuitVersion` to + `orchard::circuit_version::OrchardCircuitVersion`. Neither it nor + `orchard::bundle::BundleVersion::circuit_version` (the method that returns it) require the + `circuit` feature anymore, so that `orchard::Proof::expected_proof_size` can take + an `OrchardCircuitVersion` argument without pulling in the `circuit` feature. +- `OrchardCircuitVersion` is now `#[non_exhaustive]`: downstream crates matching on it must + add a wildcard arm. +- `orchard::circuit::Circuit` no longer implements `halo2_proofs::plonk::Circuit`. It still + carries the witnesses of a single action, but the `plonk::Circuit` implementations now live + on the crate-internal `CircuitVanilla` and `CircuitZsa` types, one per circuit variation. + `Proof::create`, `ProvingKey::build`, and `VerifyingKey::build` dispatch to the right one + internally, so callers do not need to select it themselves. +- `orchard::Note::nullifier` now implements the split-note nullifier derivation of ZIP 226: a + note carrying a split seed derives $\psi$ from that seed, and its nullifier is offset by + $\mathcal{L}^{\mathsf{Orchard}}$. No public constructor produces a note with a split seed, + so the nullifier of every note that can be built today is unchanged. +- `orchard::circuit::Config` is now generic over the `halo2_gadgets` lookup-range-check + strategy. +- `unstable-voting-circuits`-only (not covered by the crate's semver guarantees): + - `orchard::circuit::note_commit::{NoteCommitConfig, NoteCommitChip}` are now generic over the + same lookup-range-check strategy, and `NoteCommitChip::configure` takes an additional + `is_zsa_circuit: bool` argument. + - Three gadgets moved out of `orchard::circuit::gadget`, each into a module of its own. Their + behavior is unchanged, but the old paths no longer exist (paths below are relative to + `orchard::circuit`): + - `gadget::derive_nullifier` → `derive_nullifier::gadgets::derive_nullifier`, plus a trailing + `zsa_params: Option` argument (`None` for vanilla behavior). + - `gadget::commit_ivk` → `commit_ivk::gadgets::commit_ivk`, now generic over the + lookup-range-check strategy and the Sinsemilla chip. + - `gadget::note_commit` → `note_commit::gadgets::note_commit`, now generic over the + lookup-range-check strategy, plus a trailing + `zsa_params: Option` argument (`None` for vanilla behavior). + - `orchard::constants::{OrchardHashDomains, OrchardCommitDomains}` both gained a + `NoteZsaCommit` variant (listed under Added) and are now `#[non_exhaustive]`, so downstream + matches on either must add a wildcard arm. + ## [0.15.5] - 2026-08-02 ### Changed diff --git a/Cargo.lock b/Cargo.lock index e532b9bab..479de4866 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,14 +954,13 @@ dependencies = [ [[package]] name = "halo2_gadgets" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2a697cad929f706b7987fe804ad57d43622cd37463ba7e4d662a926fdcfea3" +source = "git+https://github.com/QED-it/halo2?rev=62fac855a606dc81e327c5b4dfc2277e3366eebb#62fac855a606dc81e327c5b4dfc2277e3366eebb" dependencies = [ "arrayvec", "bitvec", "ff", "group", - "halo2_poseidon", + "halo2_poseidon 0.1.0 (git+https://github.com/QED-it/halo2?rev=62fac855a606dc81e327c5b4dfc2277e3366eebb)", "halo2_proofs", "lazy_static", "pasta_curves", @@ -990,11 +989,21 @@ dependencies = [ "pasta_curves", ] +[[package]] +name = "halo2_poseidon" +version = "0.1.0" +source = "git+https://github.com/QED-it/halo2?rev=62fac855a606dc81e327c5b4dfc2277e3366eebb#62fac855a606dc81e327c5b4dfc2277e3366eebb" +dependencies = [ + "bitvec", + "ff", + "group", + "pasta_curves", +] + [[package]] name = "halo2_proofs" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5aca1c66059a919227dec97444a11a4350d2f9c820ca48690988f0aa0e81cbf" +source = "git+https://github.com/QED-it/halo2?rev=62fac855a606dc81e327c5b4dfc2277e3366eebb#62fac855a606dc81e327c5b4dfc2277e3366eebb" dependencies = [ "blake2b_simd", "ff", @@ -1444,7 +1453,7 @@ dependencies = [ "getset", "group", "halo2_gadgets", - "halo2_poseidon", + "halo2_poseidon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "halo2_proofs", "hex", "image", @@ -2020,8 +2029,7 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "sinsemilla" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d268ae0ea06faafe1662e9967cd4f9022014f5eeb798e0c302c876df8b7af9c" +source = "git+https://github.com/zcash/sinsemilla?rev=aabb707e862bc3d7b803c77d14e5a771bcee3e8c#aabb707e862bc3d7b803c77d14e5a771bcee3e8c" dependencies = [ "group", "pasta_curves", diff --git a/Cargo.toml b/Cargo.toml index 3e7159909..35046a0cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,3 +112,8 @@ debug = true [profile.bench] debug = true + +[patch.crates-io] +sinsemilla = { git = "https://github.com/zcash/sinsemilla", rev = "aabb707e862bc3d7b803c77d14e5a771bcee3e8c" } +halo2_gadgets = { git = "https://github.com/QED-it/halo2", rev = "62fac855a606dc81e327c5b4dfc2277e3366eebb" } +halo2_proofs = { git = "https://github.com/QED-it/halo2", rev = "62fac855a606dc81e327c5b4dfc2277e3366eebb" } diff --git a/benches/circuit.rs b/benches/circuit.rs index b83958978..ed21ceb73 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -9,7 +9,8 @@ use pprof::criterion::{Output, PProfProfiler}; use orchard::{ builder::{Builder, BundleType}, bundle::BundleVersion, - circuit::{OrchardCircuitVersion, ProvingKey, VerifyingKey}, + circuit::{ProvingKey, VerifyingKey}, + circuit_version::OrchardCircuitVersion, keys::{FullViewingKey, Scope, SpendingKey}, value::NoteValue, Anchor, Bundle, diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index 44db99f25..7888348b4 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -2,7 +2,8 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Through use orchard::{ builder::{Builder, BundleType}, bundle::BundleVersion, - circuit::{OrchardCircuitVersion, ProvingKey}, + circuit::ProvingKey, + circuit_version::OrchardCircuitVersion, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey}, note_encryption::{CompactAction, OrchardDomain}, value::NoteValue, diff --git a/src/action.rs b/src/action.rs index 22df672cd..d93601e3b 100644 --- a/src/action.rs +++ b/src/action.rs @@ -196,7 +196,7 @@ pub(crate) mod testing { use crate::{ note::{ commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier, - testing::arb_note, TransmittedNoteCiphertext, + testing::arb_note, AssetBase, TransmittedNoteCiphertext, }, note_encryption::{OrchardDomain, OrchardNoteEncryption}, primitives::redpallas::{self, testing::arb_valid_spendauth_keypair}, @@ -235,9 +235,11 @@ pub(crate) mod testing { rng_seed in prop::array::uniform32(prop::num::u8::ANY), ) -> Action<()> { let cmx = ExtractedNoteCommitment::from(note.commitment()); - let cv_net = ValueCommitment::derive( + let cv_net = ValueCommitment::derive_with_asset( spend_value - output_value, - ValueCommitTrapdoor::zero() + ValueCommitTrapdoor::zero(), + // TODO ZSA: asset should be a param, not hardcoded here + AssetBase::zatoshi() ); let encrypted_note = encrypted_note_for(note, &cv_net, &cmx, StdRng::from_seed(rng_seed)); @@ -263,9 +265,11 @@ pub(crate) mod testing { fake_sighash in prop::array::uniform32(prop::num::u8::ANY), ) -> Action> { let cmx = ExtractedNoteCommitment::from(note.commitment()); - let cv_net = ValueCommitment::derive( + let cv_net = ValueCommitment::derive_with_asset( spend_value - output_value, - ValueCommitTrapdoor::zero() + ValueCommitTrapdoor::zero(), + // TODO ZSA: asset should be a param, not hardcoded here + AssetBase::zatoshi() ); let encrypted_note = diff --git a/src/builder.rs b/src/builder.rs index 14a40eaf3..d7243a31e 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -17,7 +17,10 @@ use crate::{ FullViewingKey, OutgoingViewingKey, Scope, SpendAuthorizingKey, SpendValidatingKey, SpendingKey, }, - note::{ExtractedNoteCommitment, Note, NoteVersion, Nullifier, Rho, TransmittedNoteCiphertext}, + note::{ + AssetBase, ExtractedNoteCommitment, Note, NoteVersion, Nullifier, Rho, + TransmittedNoteCiphertext, + }, note_encryption::OrchardNoteEncryption, primitives::redpallas::{self, Binding, SpendAuth}, tree::{Anchor, MerklePath}, @@ -29,7 +32,8 @@ use crate::{ use { crate::{ action::Action, - circuit::{Circuit, Instance, OrchardCircuitVersion, ProvingKey}, + circuit::{Circuit, Instance, ProvingKey}, + circuit_version::OrchardCircuitVersion, }, nonempty::NonEmpty, }; @@ -352,6 +356,9 @@ pub struct SpendInfo { pub(crate) scope: Scope, pub(crate) note: Note, pub(crate) merkle_path: Option, + // If `split_flag` is true, the spend note's value is not counted in the action's `ValueSum`. + // TODO ZSA: `ActionInfo::value_sum` does not implement this yet. + pub(crate) split_flag: bool, } impl SpendInfo { @@ -372,6 +379,7 @@ impl SpendInfo { scope, note, merkle_path: Some(merkle_path), + split_flag: false, }) } @@ -392,6 +400,7 @@ impl SpendInfo { scope, note, merkle_path: None, + split_flag: false, }) } @@ -410,6 +419,7 @@ impl SpendInfo { scope: Scope::External, note, merkle_path, + split_flag: false, } } @@ -484,6 +494,7 @@ pub struct OutputInfo { ovk: Option, recipient: Address, value: NoteValue, + asset: AssetBase, memo: [u8; 512], note_version: NoteVersion, /// When set, `build` fills `enc_ciphertext` with random bytes instead of encrypting the @@ -514,6 +525,8 @@ impl OutputInfo { ovk, recipient, value, + // TODO ZSA: asset should be a param, not hardcoded here + asset: AssetBase::zatoshi(), memo, note_version, randomized_ciphertext: false, @@ -536,6 +549,8 @@ impl OutputInfo { ovk: None, recipient, value: NoteValue::ZERO, + // TODO ZSA: asset should be a param, not hardcoded here + asset: AssetBase::zatoshi(), memo: [0u8; 512], note_version, randomized_ciphertext: matches!(spent_scope, Scope::External), @@ -679,7 +694,18 @@ struct ActionInfo { } impl ActionInfo { + /// # Panics + /// + /// Panics if the spent and output notes do not have the same asset. The circuit + /// witnesses a single `asset`, used for both note commitments and for the value + /// commitment, so an action cannot mix assets. fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self { + assert_eq!( + spend.note.asset(), + output.asset, + "an action's spent and output notes must have the same asset" + ); + ActionInfo { spend, output, @@ -706,7 +732,8 @@ impl ActionInfo { circuit_version: OrchardCircuitVersion, ) -> (Action, Circuit) { let v_net = self.value_sum(); - let cv_net = ValueCommitment::derive(v_net, self.rcv.clone()); + let cv_net = + ValueCommitment::derive_with_asset(v_net, self.rcv.clone(), self.spend.note.asset()); let (nf_old, ak, alpha, rk) = self.spend.build(&mut rng); let (note, cmx, encrypted_note) = self.output.build(&cv_net, nf_old, &mut rng); @@ -738,8 +765,11 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { + // ZSA notes are not yet supported by PCZT. + assert_eq!(self.spend.note.asset(), AssetBase::zatoshi()); let v_net = self.value_sum(); - let cv_net = ValueCommitment::derive(v_net, self.rcv.clone()); + let cv_net = + ValueCommitment::derive_with_asset(v_net, self.rcv.clone(), self.spend.note.asset()); let spend = self.spend.into_pczt(&mut rng); let output = self.output.into_pczt(&cv_net, spend.nullifier, &mut rng); @@ -1427,6 +1457,7 @@ fn build_bundle( scope, note, merkle_path: Some(MerklePath::dummy(&mut rng)), + split_flag: false, }; pairs.push((None, Some(chg_idx), spend, output)); } @@ -1880,7 +1911,8 @@ pub mod testing { use crate::{ address::testing::arb_address, bundle::{Authorized, Bundle, BundleVersion, TxVersion}, - circuit::{OrchardCircuitVersion, ProvingKey}, + circuit::ProvingKey, + circuit_version::OrchardCircuitVersion, keys::{ testing::arb_spending_key, FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey, }, @@ -2166,7 +2198,8 @@ mod tests { use crate::{ builder::{BundleType, SpendError}, bundle::{Authorized, Bundle, BundleVersion, Flags, TxVersion}, - circuit::{OrchardCircuitVersion, ProvingKey}, + circuit::ProvingKey, + circuit_version::OrchardCircuitVersion, constants::MERKLE_DEPTH_ORCHARD, keys::{ FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey, diff --git a/src/bundle.rs b/src/bundle.rs index 1fc4eb57b..381396576 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -22,6 +22,7 @@ use crate::{ action::Action, address::Address, bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data}, + circuit_version::OrchardCircuitVersion, keys::{IncomingViewingKey, OutgoingViewingKey, PreparedIncomingViewingKey}, note::{Note, NoteVersion}, note_encryption::BundleDomain, @@ -32,7 +33,7 @@ use crate::{ }; #[cfg(feature = "circuit")] -use crate::circuit::{Instance, OrchardCircuitVersion, VerifyingKey}; +use crate::circuit::{Instance, VerifyingKey}; #[cfg(feature = "circuit")] impl Action { @@ -129,7 +130,6 @@ impl BundleVersion { /// under [`ProtocolVersion::V3`] share the post-NU6.3 circuit, so build a key with /// `ProvingKey::build(bundle_version.circuit_version())` / /// `VerifyingKey::build(bundle_version.circuit_version())`. - #[cfg(feature = "circuit")] pub fn circuit_version(&self) -> OrchardCircuitVersion { match self.protocol_version { ProtocolVersion::InsecureV1 => OrchardCircuitVersion::InsecurePreNu6_2, @@ -164,6 +164,12 @@ impl BundleVersion { ) } + /// Whether the consensus rules for this version *permit* zsa transfers. + pub(crate) fn permits_zsa(&self) -> bool { + // TODO ZSA: return true only for bundle versions that support ZSA + false + } + /// The default [`Flags`] for a bundle of this version: spends and outputs enabled, with the /// cross-address bit set to the least-restrictive value the version permits (enabled unless /// the version mandates the restriction). @@ -236,13 +242,25 @@ pub struct Flags { /// expanded receiver as the note it spends; proving and verification must reject the /// bundle unless they use a circuit key that supports the restriction. cross_address_enabled: bool, + /// Flag denoting whether ZSA functionality is enabled in the transaction. + /// + /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are + /// guaranteed to be notes with zatoshi asset. If `true`, `Action`s may use any asset. + /// + /// This field was introduced with the ZSA feature; older Orchard versions did not + /// include it. Because halo2_proofs zero-extends instance values, old proofs are interpreted + /// with this flag equal to zero (`false`), so adding it does not break consensus. + zsa_enabled: bool, } const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001; const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010; const FLAG_V6_CROSS_ADDRESS_ENABLED: u8 = 0b0000_0100; -const FLAGS_ALWAYS_EXPECTED_UNSET: u8 = - !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_V6_CROSS_ADDRESS_ENABLED); +const FLAG_ZSA_ENABLED: u8 = 0b0000_1000; +const FLAGS_ALWAYS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED + | FLAG_OUTPUTS_ENABLED + | FLAG_V6_CROSS_ADDRESS_ENABLED + | FLAG_ZSA_ENABLED); impl Flags { /// Construct a set of flags from its constituent parts, including the cross-address bit. @@ -258,6 +276,8 @@ impl Flags { spends_enabled, outputs_enabled, cross_address_enabled, + // TODO ZSA: zsa_enabled should be a param, not hardcoded here + zsa_enabled: false, } } @@ -272,6 +292,7 @@ impl Flags { spends_enabled: true, outputs_enabled: true, cross_address_enabled: true, + zsa_enabled: false, }; /// The flag set for a bundle that may create notes but not spend them: every @@ -283,6 +304,7 @@ impl Flags { spends_enabled: false, outputs_enabled: true, cross_address_enabled: true, + zsa_enabled: false, }; /// The flag set for a bundle that may spend notes but not create them: every @@ -294,6 +316,7 @@ impl Flags { spends_enabled: true, outputs_enabled: false, cross_address_enabled: true, + zsa_enabled: false, }; /// The flag set with spends and outputs enabled and cross-address transfers disabled. @@ -304,6 +327,7 @@ impl Flags { spends_enabled: true, outputs_enabled: true, cross_address_enabled: false, + zsa_enabled: false, }; /// Flag denoting whether Orchard spends are enabled in the transaction. @@ -334,6 +358,14 @@ impl Flags { self.cross_address_enabled } + /// Flag denoting whether ZSA functionality is enabled in the transaction. + /// + /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are + /// guaranteed to be notes with zatoshi asset. If `true`, `Action`s may use any asset. + pub fn zsa_enabled(&self) -> bool { + self.zsa_enabled + } + /// Serialize flags to a byte as defined in [Zcash Protocol Spec § 7.1: Transaction /// Encoding And Consensus][txencoding]. /// @@ -375,6 +407,13 @@ impl Flags { (ValuePool::Ironwood, _) => return None, } + if self.zsa_enabled { + if !bundle_version.permits_zsa() { + return None; + } + value |= FLAG_ZSA_ENABLED; + } + Some(value) } @@ -398,7 +437,7 @@ impl Flags { /// /// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding pub fn from_byte(value: u8, bundle_version: BundleVersion) -> Option { - // Bits 3..=7 are always reserved and MUST be 0. + // Bits 4..=7 are always reserved and MUST be 0. // https://p.z.cash/TCR:bad-txns-v5-reserved-bits-nonzero if value & FLAGS_ALWAYS_EXPECTED_UNSET != 0 { return None; @@ -421,10 +460,19 @@ impl Flags { ProtocolVersion::InsecureV1 | ProtocolVersion::V2 => true, ProtocolVersion::V3 => bit2, }; + + // Bit 3 (`zsa_enabled`) can only be 1 for a bundle version that permits ZSA + // transfers; it MUST be 0 otherwise. + let bit3 = value & FLAG_ZSA_ENABLED != 0; + if bit3 && !bundle_version.permits_zsa() { + return None; + } + Some(Self { spends_enabled: value & FLAG_SPENDS_ENABLED != 0, outputs_enabled: value & FLAG_OUTPUTS_ENABLED != 0, cross_address_enabled, + zsa_enabled: bit3, }) } } @@ -486,8 +534,12 @@ impl fmt::Debug for Bundle { /// Returns [`BundleError::NonCanonicalProofSize`] if it does not. This is the shared check /// used by the proof-carrying bundle constructors to reject non-canonical (e.g. padded) /// proofs; see [`Bundle::try_from_parts`] (GHSA-2x4w-pxqw-58v9). -pub(crate) fn validate_proof_size(proof: &Proof, num_actions: usize) -> Result<(), BundleError> { - let expected = Proof::expected_proof_size(num_actions); +pub(crate) fn validate_proof_size( + circuit_version: OrchardCircuitVersion, + proof: &Proof, + num_actions: usize, +) -> Result<(), BundleError> { + let expected = Proof::expected_proof_size(circuit_version, num_actions); let actual = proof.as_ref().len(); if actual == expected { Ok(()) @@ -948,7 +1000,11 @@ impl Bundle { bundle_version: BundleVersion, ) -> Result { if bundle_version.enforces_canonical_proof_size() { - validate_proof_size(authorization.proof(), actions.len())?; + validate_proof_size( + bundle_version.circuit_version(), + authorization.proof(), + actions.len(), + )?; } validate_flags(&flags, bundle_version)?; Ok(Bundle::from_parts_unchecked( @@ -989,7 +1045,7 @@ impl Bundle { /// /// Also returns an error if proof verification fails. /// - /// [`OrchardCircuitVersion::PostNu6_3`]: crate::circuit::OrchardCircuitVersion::PostNu6_3 + /// [`OrchardCircuitVersion::PostNu6_3`]: crate::circuit_version::OrchardCircuitVersion::PostNu6_3 #[cfg(feature = "circuit")] pub fn verify_proof(&self, vk: &VerifyingKey) -> Result<(), halo2_proofs::plonk::Error> { self.authorization() @@ -1168,6 +1224,7 @@ pub mod testing { spends_enabled, outputs_enabled, cross_address_enabled, + zsa_enabled: false, } } } @@ -1226,7 +1283,7 @@ pub mod testing { sk in arb_binding_signing_key(), rng_seed in prop::array::uniform32(prop::num::u8::ANY), // A fake proof of the canonical length, so the bundle passes `try_from_parts`. - fake_proof in vec(prop::num::u8::ANY, Proof::expected_proof_size(n_actions)), + fake_proof in vec(prop::num::u8::ANY, Proof::expected_proof_size(bundle_version.circuit_version(), n_actions)), fake_sighash in prop::array::uniform32(prop::num::u8::ANY), flags in Just(flags), bundle_version in Just(bundle_version), @@ -1402,23 +1459,33 @@ pub(crate) mod tests { ); } - // Bits 3.. are always reserved, in every NU6.3 pool. - for value in 0b1000..=u8::MAX { + // Bit 3 (`zsa_enabled`) is reserved until some bundle version's `permits_zsa()` + // returns true; today no version does, so every value with bit 3 set is rejected. + for value in 0b1000..0b10000 { + assert_eq!(Flags::from_byte(value, BundleVersion::orchard_v3()), None); + assert_eq!(Flags::from_byte(value, BundleVersion::ironwood_v3()), None); + } + + // Bits 4.. are always reserved, in every NU6.3 pool. + for value in 0b10000..=u8::MAX { assert_eq!(Flags::from_byte(value, BundleVersion::orchard_v3()), None); assert_eq!(Flags::from_byte(value, BundleVersion::ironwood_v3()), None); } } #[test] - fn expected_proof_size_matches_known_values() { + fn expected_proof_size_matches_known_values_ironwood_v3() { // The canonical proof sizes for one and two actions, fixed by the action circuit. - assert_eq!(Proof::expected_proof_size(1), 4992); - assert_eq!(Proof::expected_proof_size(2), 7264); + let circuit_version = BundleVersion::ironwood_v3().circuit_version(); + assert_eq!(Proof::expected_proof_size(circuit_version, 1), 4992); + assert_eq!(Proof::expected_proof_size(circuit_version, 2), 7264); // The size is affine in the number of actions: each action contributes a fixed amount. - let per_action = Proof::expected_proof_size(2) - Proof::expected_proof_size(1); + let per_action = Proof::expected_proof_size(circuit_version, 2) + - Proof::expected_proof_size(circuit_version, 1); assert_eq!( - Proof::expected_proof_size(3) - Proof::expected_proof_size(2), + Proof::expected_proof_size(circuit_version, 3) + - Proof::expected_proof_size(circuit_version, 2), per_action, ); } @@ -1626,10 +1693,10 @@ pub(crate) mod tests { bundle in arb_bundle(3) ) { let actions = bundle.actions().clone(); - let expected = Proof::expected_proof_size(actions.len()); let flags = *bundle.flags(); // Ironwood enforces canonical proof size and accepts any cross-address flag value. let bundle_version = BundleVersion::ironwood_v3(); + let expected = Proof::expected_proof_size(bundle_version.circuit_version(), actions.len()); let value_balance = *bundle.value_balance(); let anchor = *bundle.anchor(); let binding_signature = bundle.authorization().binding_signature().clone(); @@ -1691,8 +1758,9 @@ pub(crate) mod tests { fn try_from_parts_checks_proof_size_with_cross_address_disabled( bundle in arb_bundle(3) ) { + let bundle_version = BundleVersion::orchard_v3(); let actions = bundle.actions().clone(); - let expected = Proof::expected_proof_size(actions.len()); + let expected = Proof::expected_proof_size(bundle_version.circuit_version(), actions.len()); let mut flags = *bundle.flags(); flags.cross_address_enabled = false; let value_balance = *bundle.value_balance(); @@ -1709,7 +1777,7 @@ pub(crate) mod tests { Proof::new(vec![0u8; expected + 1]), binding_signature, ), - BundleVersion::orchard_v3(), + bundle_version, ) .err(), Some(BundleError::NonCanonicalProofSize { expected, actual: expected + 1 }) @@ -1721,7 +1789,8 @@ pub(crate) mod tests { // The historical pre-NU6.2 Orchard pool does not enforce canonical proof size, so a // padded proof is accepted: its transaction is already committed and cannot be // re-canonicalized. - let expected = Proof::expected_proof_size(bundle.actions().len()); + let bundle_version = BundleVersion::orchard_insecure_v1(); + let expected = Proof::expected_proof_size(bundle_version.circuit_version(), bundle.actions().len()); let padded = Bundle::try_from_parts( bundle.actions().clone(), Flags::ENABLED, @@ -1731,7 +1800,7 @@ pub(crate) mod tests { Proof::new(vec![0u8; expected + 1]), bundle.authorization().binding_signature().clone(), ), - BundleVersion::orchard_insecure_v1(), + bundle_version, ); prop_assert!(padded.is_ok()); } @@ -1769,8 +1838,8 @@ pub(crate) mod tests { let bundle = with_cross_address_disabled(sample_authorized_bundle(1)); for circuit_version in [ - crate::circuit::OrchardCircuitVersion::InsecurePreNu6_2, - crate::circuit::OrchardCircuitVersion::FixedPostNu6_2, + crate::circuit_version::OrchardCircuitVersion::InsecurePreNu6_2, + crate::circuit_version::OrchardCircuitVersion::FixedPostNu6_2, ] { let vk = crate::circuit::VerifyingKey::build(circuit_version); diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index 6b502dfc0..b45460070 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -24,11 +24,17 @@ struct BundleSignature { #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum BatchError { - /// The bundle disables cross-address transfers, but the validator's verifying key's - /// circuit version does not constrain the cross-address restriction (see - /// [`OrchardCircuitVersion::supports_cross_address_restriction`]). + /// The bundle's flags require a circuit capability the validator's verifying key does + /// not provide. Returned in either of these cases: /// - /// [`OrchardCircuitVersion::supports_cross_address_restriction`]: crate::circuit::OrchardCircuitVersion::supports_cross_address_restriction + /// - the bundle disables cross-address transfers, but the key's circuit version does not + /// constrain the cross-address restriction (see + /// [`OrchardCircuitVersion::supports_cross_address_restriction`]); + /// - the bundle enables ZSA, but the key's circuit version is not + /// [`OrchardCircuitVersion::ZSA`]. + /// + /// [`OrchardCircuitVersion::supports_cross_address_restriction`]: crate::circuit_version::OrchardCircuitVersion::supports_cross_address_restriction + /// [`OrchardCircuitVersion::ZSA`]: crate::circuit_version::OrchardCircuitVersion::ZSA RestrictionUnsupportedByKey, } @@ -75,7 +81,9 @@ impl<'a> BatchValidator<'a> { /// /// Returns [`BatchError::RestrictionUnsupportedByKey`] if the bundle disables cross-address /// transfers but the validator's verifying key's circuit version does not support the - /// cross-address restriction; in that case the bundle is not added to the batch. + /// cross-address restriction, or if the bundle enables ZSA but the validator's verifying + /// key's circuit version does not support ZSA; in either case the bundle is not added to + /// the batch. pub fn add_bundle>( &mut self, bundle: &Bundle, @@ -86,6 +94,10 @@ impl<'a> BatchValidator<'a> { return Err(BatchError::RestrictionUnsupportedByKey); } + if bundle.flags().zsa_enabled() && !self.vk.circuit_version().is_zsa() { + return Err(BatchError::RestrictionUnsupportedByKey); + } + for action in bundle.actions().iter() { self.signatures.push(BundleSignature { signature: action @@ -151,7 +163,8 @@ mod tests { use super::{BatchError, BatchValidator}; use crate::{ bundle::tests::{sample_authorized_bundle, with_cross_address_disabled}, - circuit::{OrchardCircuitVersion, VerifyingKey}, + circuit::VerifyingKey, + circuit_version::OrchardCircuitVersion, }; #[test] @@ -180,6 +193,33 @@ mod tests { assert_eq!(validator.add_bundle(&bundle, [0; 32]), Ok(())); } + #[test] + fn add_bundle_rejects_unsupported_zsa() { + let mut bundle = sample_authorized_bundle(1) + .try_map_value_balance(i64::try_from) + .expect("generated bundle value balance fits in i64"); + bundle.flags.zsa_enabled = true; + // Cross-address stays enabled, so ZSA is the only capability a key can lack. + bundle.flags.cross_address_enabled = true; + + // Only a ZSA key constrains the ZSA statement; every other key rejects at insertion. + for circuit_version in [ + OrchardCircuitVersion::FixedPostNu6_2, + OrchardCircuitVersion::PostNu6_3, + ] { + let vk = VerifyingKey::build(circuit_version); + let mut validator = BatchValidator::new(&vk); + assert_eq!( + validator.add_bundle(&bundle, [0; 32]), + Err(BatchError::RestrictionUnsupportedByKey) + ); + } + + let vk = VerifyingKey::build(OrchardCircuitVersion::ZSA); + let mut validator = BatchValidator::new(&vk); + assert_eq!(validator.add_bundle(&bundle, [0; 32]), Ok(())); + } + #[test] fn empty_batch_validates() { for circuit_version in [ diff --git a/src/circuit.rs b/src/circuit.rs index 06ce285c4..77545fbe4 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,74 +1,69 @@ //! The Orchard Action circuit implementation. +//! +//! This module defines the common structures, traits and implementations for the +//! Orchard Action circuit, supporting both the standard ("Vanilla") and ZSA variations. use alloc::vec::Vec; use group::{Curve, GroupEncoding}; use halo2_proofs::{ - circuit::{floor_planner, Layouter, Value}, + circuit::Value, plonk::{ - self, Advice, BatchVerifier, Column, Constraints, Expression, Instance as InstanceColumn, - Selector, SingleVerifier, + self, Advice, BatchVerifier, Column, Instance as InstanceColumn, Selector, SingleVerifier, }, - poly::Rotation, transcript::{Blake2bRead, Blake2bWrite}, }; use pasta_curves::{arithmetic::CurveAffine, pallas, vesta}; use rand::RngCore; -use self::{ - commit_ivk::{CommitIvkChip, CommitIvkConfig}, - gadget::{ - add_chip::{AddChip, AddConfig}, - assign_free_advice, - }, - note_commit::{NoteCommitChip, NoteCommitConfig}, -}; use crate::{ builder::SpendInfo, bundle::Flags, - constants::{ - OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, - MERKLE_DEPTH_ORCHARD, - }, - keys::{ - CommitIvkRandomness, DiversifiedTransmissionKey, NullifierDerivingKey, SpendValidatingKey, - }, - note::{ - commitment::{NoteCommitTrapdoor, NoteCommitment}, - nullifier::Nullifier, - ExtractedNoteCommitment, Note, Rho, + circuit::{ + commit_ivk::{CommitIvkChip, CommitIvkConfig}, + gadget::add_chip::{AddChip, AddConfig}, + note_commit::{NoteCommitChip, NoteCommitConfig}, }, + circuit_version::OrchardCircuitVersion, + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, + note::{nullifier::Nullifier, ExtractedNoteCommitment, Note, Rho}, primitives::redpallas::{SpendAuth, VerificationKey}, - spec::NonIdentityPallasPoint, - tree::{Anchor, MerkleHashOrchard}, - value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + tree::Anchor, + value::{ValueCommitTrapdoor, ValueCommitment}, }; use halo2_gadgets::{ ecc::{ chip::{EccChip, EccConfig}, - CircuitVersion, FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarFixedShort, - ScalarVar, + CircuitVersion, NonIdentityPoint, }, poseidon::{primitives as poseidon, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, sinsemilla::{ chip::{SinsemillaChip, SinsemillaConfig}, - merkle::{ - chip::{MerkleChip, MerkleConfig}, - MerklePath, - }, + merkle::chip::{MerkleChip, MerkleConfig}, }, - utilities::lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, + utilities::lookup_range_check::PallasLookupRangeCheck, }; +mod circuit_vanilla; +mod circuit_zsa; + +use circuit_vanilla::CircuitVanilla; +use circuit_zsa::{AdditionalZsaWitnesses, CircuitZsa}; + #[cfg(not(feature = "unstable-voting-circuits"))] -mod commit_ivk; +pub(in crate::circuit) mod commit_ivk; #[cfg(feature = "unstable-voting-circuits")] pub mod commit_ivk; +#[cfg(not(feature = "unstable-voting-circuits"))] +pub(in crate::circuit) mod derive_nullifier; +#[cfg(feature = "unstable-voting-circuits")] +pub mod derive_nullifier; pub mod gadget; #[cfg(not(feature = "unstable-voting-circuits"))] -mod note_commit; +pub(in crate::circuit) mod note_commit; #[cfg(feature = "unstable-voting-circuits")] pub mod note_commit; +pub(in crate::circuit) mod value_commit_orchard; pub use crate::Proof; @@ -86,60 +81,28 @@ const CMX: usize = 6; const ENABLE_SPEND: usize = 7; const ENABLE_OUTPUT: usize = 8; const DISABLE_CROSS_ADDRESS: usize = 9; +const ENABLE_ZSA: usize = 10; /// Configuration needed to use the Orchard Action circuit. #[derive(Clone, Debug)] -pub struct Config { +pub struct Config { primary: Column, q_orchard: Selector, advices: [Column; 10], add_config: AddConfig, - ecc_config: EccConfig, + ecc_config: EccConfig, poseidon_config: PoseidonConfig, - merkle_config_1: MerkleConfig, - merkle_config_2: MerkleConfig, + merkle_config_1: + MerkleConfig, + merkle_config_2: + MerkleConfig, sinsemilla_config_1: - SinsemillaConfig, + SinsemillaConfig, sinsemilla_config_2: - SinsemillaConfig, + SinsemillaConfig, commit_ivk_config: CommitIvkConfig, - old_note_commit_config: NoteCommitConfig, - new_note_commit_config: NoteCommitConfig, -} - -/// Selects which version of the Orchard Action circuit to build. -/// -/// [`FixedPostNu6_2`] and [`InsecurePreNu6_2`] produce different verifying keys: the fixed -/// circuit anchors the variable-base scalar-multiplication base (see `halo2_gadgets`), while -/// the pre-NU6.2 one does not. [`PostNu6_3`] extends the fixed circuit by enforcing the -/// same-address check, i.e. `(g_d^old, pk_d^old) = (g_d^new, pk_d^new)`, when the -/// boolean `disableCrossAddress` public input is set. -/// -/// This is a runtime value rather than a type parameter: it is carried in [`Circuit`] and -/// chosen when building a [`ProvingKey`] or [`VerifyingKey`], so the circuit version can be -/// threaded dynamically (e.g. across an FFI boundary). -/// -/// Please note that the public exposure of APIs using `InsecurePreNu6_2` is intentional, -/// and is strictly necessary for verifying the block chain from NU5 activation and for -/// creating proofs needed by tests that operate at past epochs. These APIs cannot be -/// used accidentally without passing an `OrchardCircuitVersion` that is clearly labelled -/// "insecure". This is not a security vulnerability. -/// -/// [`FixedPostNu6_2`]: OrchardCircuitVersion::FixedPostNu6_2 -/// [`InsecurePreNu6_2`]: OrchardCircuitVersion::InsecurePreNu6_2 -/// [`PostNu6_3`]: OrchardCircuitVersion::PostNu6_3 -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum OrchardCircuitVersion { - /// The insecure pre-NU6.2 circuit, in which the variable-base scalar-multiplication base - /// is not anchored to the real base. For reconstructing the historical (NU5..NU6.2) - /// verifying key only — never for proving or current verification. - InsecurePreNu6_2, - /// The fixed circuit, active from NU6.2 onward. Used for all current network proving and - /// verification. - FixedPostNu6_2, - /// The post-NU 6.3 circuit. This uses the fixed circuit with additional constraints - /// enforcing the `disableCrossAddress` public input. - PostNu6_3, + old_note_commit_config: NoteCommitConfig, + new_note_commit_config: NoteCommitConfig, } impl OrchardCircuitVersion { @@ -156,7 +119,7 @@ impl OrchardCircuitVersion { OrchardCircuitVersion::InsecurePreNu6_2 | OrchardCircuitVersion::FixedPostNu6_2 => { false } - OrchardCircuitVersion::PostNu6_3 => true, + OrchardCircuitVersion::PostNu6_3 | OrchardCircuitVersion::ZSA => true, } } @@ -164,69 +127,54 @@ impl OrchardCircuitVersion { fn halo2_version(self) -> CircuitVersion { match self { OrchardCircuitVersion::InsecurePreNu6_2 => CircuitVersion::InsecureUnanchoredBase, - OrchardCircuitVersion::FixedPostNu6_2 | OrchardCircuitVersion::PostNu6_3 => { - CircuitVersion::AnchoredBase - } + OrchardCircuitVersion::FixedPostNu6_2 + | OrchardCircuitVersion::PostNu6_3 + | OrchardCircuitVersion::ZSA => CircuitVersion::AnchoredBase, } } + + pub(crate) fn is_zsa(self) -> bool { + self == OrchardCircuitVersion::ZSA + } } /// The Orchard Action circuit. /// -/// The `circuit_version` field selects which circuit to build. Callers must choose it -/// explicitly instead of relying on a default. +/// Carries the private witnesses of a single action for any [`OrchardCircuitVersion`]. +/// The ZSA-specific witnesses are populated only for [`OrchardCircuitVersion::ZSA`]; +/// the Vanilla circuit versions leave them unknown and do not prove them. #[derive(Clone, Debug)] pub struct Circuit { - pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, - pub(crate) pos: Value, - pub(crate) g_d_old: Value, - pub(crate) pk_d_old: Value, - pub(crate) v_old: Value, - pub(crate) rho_old: Value, - pub(crate) psi_old: Value, - pub(crate) rcm_old: Value, - pub(crate) cm_old: Value, - pub(crate) alpha: Value, - pub(crate) ak: Value, - pub(crate) nk: Value, - pub(crate) rivk: Value, - pub(crate) g_d_new: Value, - pub(crate) pk_d_new: Value, - pub(crate) v_new: Value, - pub(crate) psi_new: Value, - pub(crate) rcm_new: Value, - pub(crate) rcv: Value, - pub(crate) circuit_version: OrchardCircuitVersion, + pub(crate) common_witnesses: CircuitVanilla, + pub(crate) additional_zsa_witnesses: Value, } impl Circuit { - /// Returns an empty circuit with all private witnesses unknown. + /// Returns the [`OrchardCircuitVersion`] this circuit instance is configured for. + fn circuit_version(&self) -> OrchardCircuitVersion { + self.common_witnesses.circuit_version + } + + /// Returns the witnesses proved by the Vanilla circuit versions. /// - /// This is used for circuit shape-dependent operations, such as generating keys - /// or rendering the circuit layout, where witness values are not required but the - /// selected circuit version still determines the configured constraints. - fn empty(circuit_version: OrchardCircuitVersion) -> Self { - Circuit { - path: Value::unknown(), - pos: Value::unknown(), - g_d_old: Value::unknown(), - pk_d_old: Value::unknown(), - v_old: Value::unknown(), - rho_old: Value::unknown(), - psi_old: Value::unknown(), - rcm_old: Value::unknown(), - cm_old: Value::unknown(), - alpha: Value::unknown(), - ak: Value::unknown(), - nk: Value::unknown(), - rivk: Value::unknown(), - g_d_new: Value::unknown(), - pk_d_new: Value::unknown(), - v_new: Value::unknown(), - psi_new: Value::unknown(), - rcm_new: Value::unknown(), - rcv: Value::unknown(), - circuit_version, + /// # Errors + /// + /// Returns [`plonk::Error::Synthesis`] if `additional_zsa_witnesses` is known: the + /// Vanilla circuit versions must never be asked to prove ZSA-specific witnesses. + fn to_vanilla(&self) -> Result { + self.additional_zsa_witnesses.error_if_known_and(|_| true)?; + Ok(self.common_witnesses.clone()) + } + + /// Returns the witnesses proved by the ZSA circuit version. + /// + /// Needs no counterpart to [`Self::to_vanilla`]'s check: if + /// `additional_zsa_witnesses` is unknown, the prover rejects the unknown assignment + /// with [`plonk::Error::Synthesis`] itself. + fn to_zsa(&self) -> CircuitZsa { + CircuitZsa { + common_witnesses: self.common_witnesses.clone(), + additional_zsa_witnesses: self.additional_zsa_witnesses.clone(), } } @@ -257,6 +205,15 @@ impl Circuit { }) } + /// # Panics + /// + /// Panics for a Vanilla `circuit_version` if the spent note's asset is not zatoshi, + /// or if `spend.split_flag` is true: those statements exist only in the ZSA circuit, + /// so a Vanilla version cannot attest to them. + /// + /// Panics for any `circuit_version` if `spend.split_flag` disagrees with whether + /// `spend.note` carries a split seed: the note-derived public nullifier and the + /// `split_flag` witness would then describe different actions. pub(crate) fn from_action_context_unchecked( spend: SpendInfo, output_note: Note, @@ -264,6 +221,23 @@ impl Circuit { rcv: ValueCommitTrapdoor, circuit_version: OrchardCircuitVersion, ) -> Circuit { + if !circuit_version.is_zsa() { + assert!( + bool::from(spend.note.asset().is_zatoshi()), + "asset must be zatoshi in OrchardVanilla circuit" + ); + assert!( + !spend.split_flag, + "split_flag must be false in OrchardVanilla circuit" + ); + } + + assert_eq!( + spend.split_flag, + bool::from(spend.note.rseed_split_note().is_some()), + "split_flag must match the presence of the note's split seed" + ); + let sender_address = spend.note.recipient(); let rho_old = spend.note.rho(); let psi_old = spend.note.psi(); @@ -278,7 +252,7 @@ impl Circuit { let psi_new = output_note.psi(); let rcm_new = output_note.rcm(); - Circuit { + let common_witnesses = CircuitVanilla { path: Value::known(merkle_path.auth_path()), pos: Value::known(merkle_path.position()), g_d_old: Value::known(sender_address.g_d()), @@ -299,777 +273,200 @@ impl Circuit { rcm_new: Value::known(rcm_new), rcv: Value::known(rcv), circuit_version, + }; + + let additional_zsa_witnesses = if circuit_version.is_zsa() { + Value::known(AdditionalZsaWitnesses { + psi_nf: spend.note.psi_nf(), + asset: spend.note.asset(), + split_flag: spend.split_flag, + }) + } else { + Value::unknown() + }; + + Circuit { + common_witnesses, + additional_zsa_witnesses, } } } -impl Config { - /// Configures the Orchard Action constraint system shared by every circuit version. - fn configure(meta: &mut plonk::ConstraintSystem) -> Self { - // Advice columns used in the Orchard circuit. - let advices = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - - // Constrain v_old - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). - // Either v_old = 0, or calculated root = anchor (https://p.z.cash/ZKS:action-merkle-path-validity?partial). - // Constrain v_old = 0 or enable_spend = 1 (https://p.z.cash/ZKS:action-enable-spend). - // Constrain v_new = 0 or enable_output = 1 (https://p.z.cash/ZKS:action-enable-output). - // - // This gate is also reused for the same-address check; see - // [`Circuit::synthesize_cross_address_checks`]. - let q_orchard = meta.selector(); - meta.create_gate("Orchard circuit checks", |meta| { - let q_orchard = meta.query_selector(q_orchard); - let v_old = meta.query_advice(advices[0], Rotation::cur()); - let v_new = meta.query_advice(advices[1], Rotation::cur()); - let magnitude = meta.query_advice(advices[2], Rotation::cur()); - let sign = meta.query_advice(advices[3], Rotation::cur()); - - let root = meta.query_advice(advices[4], Rotation::cur()); - let anchor = meta.query_advice(advices[5], Rotation::cur()); - - let enable_spend = meta.query_advice(advices[6], Rotation::cur()); - let enable_output = meta.query_advice(advices[7], Rotation::cur()); - - let one = Expression::Constant(pallas::Base::one()); - - Constraints::with_selector( - q_orchard, - [ - ( - "v_old - v_new = magnitude * sign", - v_old.clone() - v_new.clone() - magnitude * sign, - ), - ( - "Either v_old = 0, or root = anchor", - v_old.clone() * (root - anchor), - ), - ( - "v_old = 0 or enable_spend = 1", - v_old * (one.clone() - enable_spend), - ), - ( - "v_new = 0 or enable_output = 1", - v_new * (one - enable_output), - ), - ], - ) - }); - - // Addition of two field elements. - let add_config = AddChip::configure(meta, advices[7], advices[8], advices[6]); - - // Fixed columns for the Sinsemilla generator lookup table - let table_idx = meta.lookup_table_column(); - let lookup = ( - table_idx, - meta.lookup_table_column(), - meta.lookup_table_column(), +/// Configures the Orchard Action circuit in the given constraint system. +/// +/// Shared by both circuit variations; `is_zsa` selects both the `q_orchard` gate +/// ([`circuit_vanilla::configure_vanilla_orchard_gate`] or +/// [`circuit_zsa::configure_zsa_orchard_gate`]) and the ZSA-specific configuration +/// of the Sinsemilla and NoteCommit chips. +fn configure_circuit( + meta: &mut plonk::ConstraintSystem, + is_zsa: bool, +) -> Config { + // Advice columns used in the Orchard circuit. + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + let q_orchard = meta.selector(); + if is_zsa { + circuit_zsa::configure_zsa_orchard_gate(meta, advices, q_orchard); + } else { + circuit_vanilla::configure_vanilla_orchard_gate(meta, advices, q_orchard); + } + + // Addition of two field elements. + let add_config = AddChip::configure(meta, advices[7], advices[8], advices[6]); + + // Fixed columns for the Sinsemilla generator lookup table + let table_idx = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + ); + + // Instance column used for public inputs + let primary = meta.instance_column(); + meta.enable_equality(primary); + + // Permutation over all advice columns. + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + // Poseidon requires four advice columns, while ECC incomplete addition requires + // six, so we could choose to configure them in parallel. However, we only use a + // single Poseidon invocation, and we have the rows to accommodate it serially. + // Instead, we reduce the proof size by sharing fixed columns between the ECC and + // Poseidon chips. + let lagrange_coeffs = [ + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ]; + let rc_a = lagrange_coeffs[2..5].try_into().unwrap(); + let rc_b = lagrange_coeffs[5..8].try_into().unwrap(); + + // Also use the first Lagrange coefficient column for loading global constants. + // It's free real estate :) + meta.enable_constant(lagrange_coeffs[0]); + + // We have a lot of free space in the right-most advice columns; use one of them + // for all of our range checks. + // The ZSA lookup argument also allocates its extra tag table column here. + let range_check = Lookup::configure(meta, advices[9], table_idx); + + // Configuration for curve point operations. + // This uses 10 advice columns and spans the whole circuit. + let ecc_config = EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ); + + // Configuration for the Poseidon hash. + let poseidon_config = PoseidonChip::configure::( + meta, + // We place the state columns after the partial_sbox column so that the + // pad-and-add region can be laid out more efficiently. + advices[6..9].try_into().unwrap(), + advices[5], + rc_a, + rc_b, + ); + + // Configuration for a Sinsemilla hash instantiation and a + // Merkle hash instantiation using this Sinsemilla instance. + // Since the Sinsemilla config uses only 5 advice columns, + // we can fit two instances side-by-side. + let (sinsemilla_config_1, merkle_config_1) = { + let sinsemilla_config_1 = SinsemillaChip::configure( + meta, + advices[..5].try_into().unwrap(), + advices[6], + lagrange_coeffs[0], + lookup, + range_check, + is_zsa, ); + let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone()); - // Instance column used for public inputs - let primary = meta.instance_column(); - meta.enable_equality(primary); - - // Permutation over all advice columns. - for advice in advices.iter() { - meta.enable_equality(*advice); - } + (sinsemilla_config_1, merkle_config_1) + }; - // Poseidon requires four advice columns, while ECC incomplete addition requires - // six, so we could choose to configure them in parallel. However, we only use a - // single Poseidon invocation, and we have the rows to accommodate it serially. - // Instead, we reduce the proof size by sharing fixed columns between the ECC and - // Poseidon chips. - let lagrange_coeffs = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - let rc_a = lagrange_coeffs[2..5].try_into().unwrap(); - let rc_b = lagrange_coeffs[5..8].try_into().unwrap(); - - // Also use the first Lagrange coefficient column for loading global constants. - // It's free real estate :) - meta.enable_constant(lagrange_coeffs[0]); - - // We have a lot of free space in the right-most advice columns; use one of them - // for all of our range checks. - let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx); - - // Configuration for curve point operations. - // This uses 10 advice columns and spans the whole circuit. - let ecc_config = - EccChip::::configure(meta, advices, lagrange_coeffs, range_check); - - // Configuration for the Poseidon hash. - let poseidon_config = PoseidonChip::configure::( + // Configuration for a Sinsemilla hash instantiation and a + // Merkle hash instantiation using this Sinsemilla instance. + // Since the Sinsemilla config uses only 5 advice columns, + // we can fit two instances side-by-side. + let (sinsemilla_config_2, merkle_config_2) = { + let sinsemilla_config_2 = SinsemillaChip::configure( meta, - // We place the state columns after the partial_sbox column so that the - // pad-and-add region can be laid out more efficiently. - advices[6..9].try_into().unwrap(), - advices[5], - rc_a, - rc_b, + advices[5..].try_into().unwrap(), + advices[7], + lagrange_coeffs[1], + lookup, + range_check, + is_zsa, ); + let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone()); - // Configuration for a Sinsemilla hash instantiation and a - // Merkle hash instantiation using this Sinsemilla instance. - // Since the Sinsemilla config uses only 5 advice columns, - // we can fit two instances side-by-side. - let (sinsemilla_config_1, merkle_config_1) = { - let sinsemilla_config_1 = SinsemillaChip::configure( - meta, - advices[..5].try_into().unwrap(), - advices[6], - lagrange_coeffs[0], - lookup, - range_check, - false, - ); - let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone()); - - (sinsemilla_config_1, merkle_config_1) - }; - - // Configuration for a Sinsemilla hash instantiation and a - // Merkle hash instantiation using this Sinsemilla instance. - // Since the Sinsemilla config uses only 5 advice columns, - // we can fit two instances side-by-side. - let (sinsemilla_config_2, merkle_config_2) = { - let sinsemilla_config_2 = SinsemillaChip::configure( - meta, - advices[5..].try_into().unwrap(), - advices[7], - lagrange_coeffs[1], - lookup, - range_check, - false, - ); - let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone()); - - (sinsemilla_config_2, merkle_config_2) - }; + (sinsemilla_config_2, merkle_config_2) + }; - // Configuration to handle decomposition and canonicity checking - // for CommitIvk. - let commit_ivk_config = CommitIvkChip::configure(meta, advices); - - // Configuration to handle decomposition and canonicity checking - // for NoteCommit_old. - let old_note_commit_config = - NoteCommitChip::configure(meta, advices, sinsemilla_config_1.clone()); - - // Configuration to handle decomposition and canonicity checking - // for NoteCommit_new. - let new_note_commit_config = - NoteCommitChip::configure(meta, advices, sinsemilla_config_2.clone()); - - Config { - primary, - q_orchard, - advices, - add_config, - ecc_config, - poseidon_config, - merkle_config_1, - merkle_config_2, - sinsemilla_config_1, - sinsemilla_config_2, - commit_ivk_config, - old_note_commit_config, - new_note_commit_config, - } + // Configuration to handle decomposition and canonicity checking + // for CommitIvk. + let commit_ivk_config = CommitIvkChip::configure(meta, advices); + + // Configuration to handle decomposition and canonicity checking + // for NoteCommit_old. + let old_note_commit_config = + NoteCommitChip::configure(meta, advices, sinsemilla_config_1.clone(), is_zsa); + + // Configuration to handle decomposition and canonicity checking + // for NoteCommit_new. + let new_note_commit_config = + NoteCommitChip::configure(meta, advices, sinsemilla_config_2.clone(), is_zsa); + + Config { + primary, + q_orchard, + advices, + add_config, + ecc_config, + poseidon_config, + merkle_config_1, + merkle_config_2, + sinsemilla_config_1, + sinsemilla_config_2, + commit_ivk_config, + old_note_commit_config, + new_note_commit_config, } } /// Cells carrying the addresses of an action's spent and newly created notes, returned /// from the shared synthesis logic so that circuit versions can impose additional /// constraints on them. -struct AddressPoints { - g_d_old: NonIdentityPoint>, - pk_d_old: NonIdentityPoint>, - g_d_new: NonIdentityPoint>, - pk_d_new: NonIdentityPoint>, -} - -impl Circuit { - /// Synthesizes the Orchard Action checks common to every circuit version, - /// parameterized by `self.circuit_version`, returning the cells carrying the old - /// and new note addresses so that circuit versions can impose additional - /// constraints on them. - #[allow(non_snake_case)] - fn synthesize_base( - &self, - config: &Config, - layouter: &mut impl Layouter, - ) -> Result { - // Load the Sinsemilla generator lookup table used by the whole circuit. - SinsemillaChip::load(config.sinsemilla_config_1.clone(), layouter)?; - - // Construct the ECC chip. - let ecc_chip = config.ecc_chip(self.circuit_version.halo2_version()); - - // Witness private inputs that are used across multiple checks. - let (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) = { - // Witness psi_old - let psi_old = assign_free_advice( - layouter.namespace(|| "witness psi_old"), - config.advices[0], - self.psi_old, - )?; - - // Witness rho_old - let rho_old = assign_free_advice( - layouter.namespace(|| "witness rho_old"), - config.advices[0], - self.rho_old.map(|rho| rho.into_inner()), - )?; - - // Witness cm_old - let cm_old = Point::new( - ecc_chip.clone(), - layouter.namespace(|| "cm_old"), - self.cm_old.as_ref().map(|cm| cm.inner().to_affine()), - )?; - - // Witness g_d_old - let g_d_old = NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "gd_old"), - self.g_d_old.as_ref().map(|gd| gd.to_affine()), - )?; - - // Witness ak_P. - let ak_P: Value = self.ak.as_ref().map(|ak| ak.into()); - let ak_P = NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "witness ak_P"), - ak_P.map(|ak_P| ak_P.to_affine()), - )?; - - // Witness nk. - let nk = assign_free_advice( - layouter.namespace(|| "witness nk"), - config.advices[0], - self.nk.map(|nk| nk.inner()), - )?; - - // Witness v_old. - let v_old = assign_free_advice( - layouter.namespace(|| "witness v_old"), - config.advices[0], - self.v_old, - )?; - - // Witness v_new. - let v_new = assign_free_advice( - layouter.namespace(|| "witness v_new"), - config.advices[0], - self.v_new, - )?; - - (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) - }; - - // Merkle path validity check (https://p.z.cash/ZKS:action-merkle-path-validity?partial). - let root = { - let path = self - .path - .map(|typed_path| typed_path.map(|node| node.inner())); - let merkle_inputs = MerklePath::construct( - [config.merkle_chip_1(), config.merkle_chip_2()], - OrchardHashDomains::MerkleCrh, - self.pos, - path, - ); - let leaf = cm_old.extract_p().inner().clone(); - merkle_inputs.calculate_root(layouter.namespace(|| "Merkle path"), leaf)? - }; - - // Value commitment integrity (https://p.z.cash/ZKS:action-cv-net-integrity?partial). - let v_net_magnitude_sign = { - // Witness the magnitude and sign of v_net = v_old - v_new - let v_net_magnitude_sign = { - let v_net = self.v_old - self.v_new; - let magnitude_sign = v_net.map(|v_net| { - let (magnitude, sign) = v_net.magnitude_sign(); - - ( - // magnitude is guaranteed to be an unsigned 64-bit value. - // Therefore, we can move it into the base field. - pallas::Base::from(magnitude), - match sign { - crate::value::Sign::Positive => pallas::Base::one(), - crate::value::Sign::Negative => -pallas::Base::one(), - }, - ) - }); - - let magnitude = assign_free_advice( - layouter.namespace(|| "v_net magnitude"), - config.advices[9], - magnitude_sign.map(|m_s| m_s.0), - )?; - let sign = assign_free_advice( - layouter.namespace(|| "v_net sign"), - config.advices[9], - magnitude_sign.map(|m_s| m_s.1), - )?; - (magnitude, sign) - }; - - let v_net = ScalarFixedShort::new( - ecc_chip.clone(), - layouter.namespace(|| "v_net"), - v_net_magnitude_sign.clone(), - )?; - let rcv = ScalarFixed::new( - ecc_chip.clone(), - layouter.namespace(|| "rcv"), - self.rcv.as_ref().map(|rcv| rcv.inner()), - )?; - - let cv_net = gadget::value_commit_orchard( - layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net)"), - ecc_chip.clone(), - v_net, - rcv, - )?; - - // Constrain cv_net to equal public input - layouter.constrain_instance(cv_net.inner().x().cell(), config.primary, CV_NET_X)?; - layouter.constrain_instance(cv_net.inner().y().cell(), config.primary, CV_NET_Y)?; - - // Return the magnitude and sign so we can use them in the Orchard gate. - v_net_magnitude_sign - }; - - // Nullifier integrity (https://p.z.cash/ZKS:action-nullifier-integrity). - let nf_old = { - let nf_old = gadget::derive_nullifier( - layouter.namespace(|| "nf_old = DeriveNullifier_nk(rho_old, psi_old, cm_old)"), - config.poseidon_chip(), - config.add_chip(), - ecc_chip.clone(), - rho_old.clone(), - &psi_old, - &cm_old, - nk.clone(), - )?; - - // Constrain nf_old to equal public input - layouter.constrain_instance(nf_old.inner().cell(), config.primary, NF_OLD)?; - - nf_old - }; - - // Spend authority (https://p.z.cash/ZKS:action-spend-authority) - { - let alpha = - ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "alpha"), self.alpha)?; - - // alpha_commitment = [alpha] SpendAuthG - let (alpha_commitment, _) = { - let spend_auth_g = OrchardFixedBasesFull::SpendAuthG; - let spend_auth_g = FixedPoint::from_inner(ecc_chip.clone(), spend_auth_g); - spend_auth_g.mul(layouter.namespace(|| "[alpha] SpendAuthG"), alpha)? - }; - - // [alpha] SpendAuthG + ak_P - let rk = alpha_commitment.add(layouter.namespace(|| "rk"), &ak_P)?; - - // Constrain rk to equal public input - layouter.constrain_instance(rk.inner().x().cell(), config.primary, RK_X)?; - layouter.constrain_instance(rk.inner().y().cell(), config.primary, RK_Y)?; - } - - // Diversified address integrity (https://p.z.cash/ZKS:action-addr-integrity?partial). - let pk_d_old = { - let ivk = { - let ak = ak_P.extract_p().inner().clone(); - let rivk = ScalarFixed::new( - ecc_chip.clone(), - layouter.namespace(|| "rivk"), - self.rivk.map(|rivk| rivk.inner()), - )?; - - gadget::commit_ivk( - config.sinsemilla_chip_1(), - ecc_chip.clone(), - config.commit_ivk_chip(), - layouter.namespace(|| "CommitIvk"), - ak, - nk, - rivk, - )? - }; - let ivk = - ScalarVar::from_base(ecc_chip.clone(), layouter.namespace(|| "ivk"), ivk.inner())?; - - // [ivk] g_d_old - // The scalar value is passed through and discarded. - let (derived_pk_d_old, _ivk) = - g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk)?; - - // Constrain derived pk_d_old to equal witnessed pk_d_old - // - // This equality constraint is technically superfluous, because the assigned - // value of `derived_pk_d_old` is an equivalent witness. But it's nice to see - // an explicit connection between circuit-synthesized values, and explicit - // prover witnesses. We could get the best of both worlds with a write-on-copy - // abstraction (https://github.com/zcash/halo2/issues/334). - let pk_d_old = NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "witness pk_d_old"), - self.pk_d_old.map(|pk_d_old| pk_d_old.inner().to_affine()), - )?; - derived_pk_d_old - .constrain_equal(layouter.namespace(|| "pk_d_old equality"), &pk_d_old)?; - - pk_d_old - }; - - // Old note commitment integrity (https://p.z.cash/ZKS:action-cm-old-integrity?partial). - { - let rcm_old = ScalarFixed::new( - ecc_chip.clone(), - layouter.namespace(|| "rcm_old"), - self.rcm_old.as_ref().map(|rcm_old| rcm_old.inner()), - )?; - - // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) - let derived_cm_old = gadget::note_commit( - layouter.namespace(|| { - "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" - }), - config.sinsemilla_chip_1(), - config.ecc_chip(self.circuit_version.halo2_version()), - config.note_commit_chip_old(), - g_d_old.inner(), - pk_d_old.inner(), - v_old.clone(), - rho_old, - psi_old, - rcm_old, - )?; - - // Constrain derived cm_old to equal witnessed cm_old - derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; - } - - // Witness g_d_new, used in the new note commitment integrity check below and, - // for the post-NU 6.3 circuit, in the cross-address checks. - let g_d_new = { - let g_d_new = self.g_d_new.map(|g_d_new| g_d_new.to_affine()); - NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "witness g_d_new_star"), - g_d_new, - )? - }; - - // Witness pk_d_new, used in the new note commitment integrity check below and, - // for the post-NU 6.3 circuit, in the cross-address checks. - let pk_d_new = { - let pk_d_new = self.pk_d_new.map(|pk_d_new| pk_d_new.inner().to_affine()); - NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "witness pk_d_new"), - pk_d_new, - )? - }; - - // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). - { - // ρ^new = nf^old - let rho_new = nf_old.inner().clone(); - - // Witness psi_new - let psi_new = assign_free_advice( - layouter.namespace(|| "witness psi_new"), - config.advices[0], - self.psi_new, - )?; - - let rcm_new = ScalarFixed::new( - ecc_chip, - layouter.namespace(|| "rcm_new"), - self.rcm_new.as_ref().map(|rcm_new| rcm_new.inner()), - )?; - - // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) - let cm_new = gadget::note_commit( - layouter.namespace(|| { - "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" - }), - config.sinsemilla_chip_2(), - config.ecc_chip(self.circuit_version.halo2_version()), - config.note_commit_chip_new(), - g_d_new.inner(), - pk_d_new.inner(), - v_new.clone(), - rho_new, - psi_new, - rcm_new, - )?; - - let cmx = cm_new.extract_p(); - - // Constrain cmx to equal public input - layouter.constrain_instance(cmx.inner().cell(), config.primary, CMX)?; - } - - // Constrain the remaining Orchard circuit checks. - layouter.assign_region( - || "Orchard circuit checks", - |mut region| { - v_old.copy_advice(|| "v_old", &mut region, config.advices[0], 0)?; - v_new.copy_advice(|| "v_new", &mut region, config.advices[1], 0)?; - v_net_magnitude_sign.0.copy_advice( - || "v_net magnitude", - &mut region, - config.advices[2], - 0, - )?; - v_net_magnitude_sign.1.copy_advice( - || "v_net sign", - &mut region, - config.advices[3], - 0, - )?; - - root.copy_advice(|| "calculated root", &mut region, config.advices[4], 0)?; - region.assign_advice_from_instance( - || "pub input anchor", - config.primary, - ANCHOR, - config.advices[5], - 0, - )?; - - region.assign_advice_from_instance( - || "enable spend", - config.primary, - ENABLE_SPEND, - config.advices[6], - 0, - )?; - - region.assign_advice_from_instance( - || "enable output", - config.primary, - ENABLE_OUTPUT, - config.advices[7], - 0, - )?; - - config.q_orchard.enable(&mut region, 0) - }, - )?; - - Ok(AddressPoints { - g_d_old, - pk_d_old, - g_d_new, - pk_d_new, - }) - } - - /// Enforces the post-NU 6.3 cross-address restriction for one action: when - /// `disableCrossAddress` is nonzero, the spent note and output note must be - /// addressed to the same expanded receiver, meaning equal `(g_d, pk_d)`. - /// - /// This reuses the existing "Orchard circuit checks" gate instead of adding a - /// new gate. The gate already has a product constraint, - /// `v_old * (root - anchor) = 0`, with exactly the shape needed for - /// `disableCrossAddress * (old_coord - new_coord) = 0`. - /// - /// The post-NU 6.3 circuit enables that gate on four extra rows, one per affine coordinate of - /// `(g_d, pk_d)`, with each row wired as: - /// - /// ```text - /// v_old <- disableCrossAddress - /// v_new <- 0 (constant) - /// magnitude <- disableCrossAddress - /// sign <- 1 (constant) - /// root <- old coordinate - /// anchor <- new coordinate - /// enable_spend <- 1 (constant) - /// enable_output <- 1 (constant) - /// ``` - /// - /// With this layout, the gate constraints become: - /// - /// ```text - /// v_old - v_new = magnitude * sign -> disableCrossAddress - 0 = disableCrossAddress * 1 - /// v_old * (root - anchor) = 0 -> disableCrossAddress * (old_coord - new_coord) = 0 - /// v_old * (1 - enable_spend) = 0 -> disableCrossAddress * (1 - 1) = 0 - /// v_new * (1 - enable_output) = 0 -> 0 * (1 - 1) = 0 - /// ``` - /// - /// The second line is the actual cross-address check. Any nonzero - /// `disableCrossAddress` value forces each old coordinate to equal the - /// corresponding new coordinate. The public API encodes `disableCrossAddress` - /// as 0 or 1, but this algebra does not rely on a boolean constraint. - /// - /// The two otherwise-unused advice columns are also filled with copies of - /// `disableCrossAddress` so these rows occupy every advice column; that prevents - /// the floor planner from overlapping another selector-enabled region with the - /// check rows. - fn synthesize_cross_address_checks( - config: &Config, - layouter: &mut impl Layouter, - addrs: &AddressPoints, - ) -> Result<(), plonk::Error> { - let AddressPoints { - g_d_old, - pk_d_old, - g_d_new, - pk_d_new, - } = addrs; - - layouter.assign_region( - || "post-NU 6.3 cross-address checks", - |mut region| { - let coordinate_checks = [ - ("g_d x", g_d_old.inner().x(), g_d_new.inner().x()), - ("g_d y", g_d_old.inner().y(), g_d_new.inner().y()), - ("pk_d x", pk_d_old.inner().x(), pk_d_new.inner().x()), - ("pk_d y", pk_d_old.inner().y(), pk_d_new.inner().y()), - ]; - - for (offset, (label, old_coord, new_coord)) in - coordinate_checks.into_iter().enumerate() - { - // Copy disableCrossAddress from the public input at - // primary[DISABLE_CROSS_ADDRESS] into advices[0] for this - // coordinate-check row. - let cross_address_disabled = region.assign_advice_from_instance( - || "disableCrossAddress", - config.primary, - DISABLE_CROSS_ADDRESS, - config.advices[0], - offset, - )?; - - // Fill the v_new, magnitude, and sign cells so the reused - // value-balance constraint reads: - // disableCrossAddress - 0 = disableCrossAddress * 1. - region.assign_advice_from_constant( - || "zero", - config.advices[1], - offset, - pallas::Base::zero(), - )?; - cross_address_disabled.copy_advice( - || "disableCrossAddress magnitude", - &mut region, - config.advices[2], - offset, - )?; - region.assign_advice_from_constant( - || "positive sign", - config.advices[3], - offset, - pallas::Base::one(), - )?; - - // Copy the old coordinate into the gate's root cell and the - // new coordinate into its anchor cell for the equality check. - old_coord.copy_advice( - || format!("old {label}"), - &mut region, - config.advices[4], - offset, - )?; - new_coord.copy_advice( - || format!("new {label}"), - &mut region, - config.advices[5], - offset, - )?; - - // Set both enable flags to one so the unrelated enable checks - // in q_orchard are neutralized on these rows. - region.assign_advice_from_constant( - || "one (neutralize enable_spend check)", - config.advices[6], - offset, - pallas::Base::one(), - )?; - region.assign_advice_from_constant( - || "one (neutralize enable_output check)", - config.advices[7], - offset, - pallas::Base::one(), - )?; - - // Occupy the otherwise-unused rightmost advice columns so the - // floor planner cannot lay out another region (and enable its - // gate) on these rows. - cross_address_disabled.copy_advice( - || "disableCrossAddress padding", - &mut region, - config.advices[8], - offset, - )?; - cross_address_disabled.copy_advice( - || "disableCrossAddress padding", - &mut region, - config.advices[9], - offset, - )?; - - config.q_orchard.enable(&mut region, offset)?; - } - - Ok(()) - }, - ) - } -} - -impl plonk::Circuit for Circuit { - type Config = Config; - type FloorPlanner = floor_planner::V1; - - fn without_witnesses(&self) -> Self { - Self::empty(self.circuit_version) - } - - fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { - Config::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), plonk::Error> { - let addrs = self.synthesize_base(&config, &mut layouter)?; - - if self.circuit_version.supports_cross_address_restriction() { - Self::synthesize_cross_address_checks(&config, &mut layouter, &addrs)?; - } - - Ok(()) - } +struct AddressPoints { + g_d_old: NonIdentityPoint>, + pk_d_old: NonIdentityPoint>, + g_d_new: NonIdentityPoint>, + pk_d_new: NonIdentityPoint>, } /// The verifying key for the Orchard Action circuit. @@ -1088,9 +485,11 @@ impl VerifyingKey { /// See [`OrchardCircuitVersion`] for which version to use. pub fn build(circuit_version: OrchardCircuitVersion) -> Self { let params = halo2_proofs::poly::commitment::Params::new(K); - let circuit = Circuit::empty(circuit_version); - - let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); + let vk = if circuit_version.is_zsa() { + plonk::keygen_vk(¶ms, &CircuitZsa::empty()).unwrap() + } else { + plonk::keygen_vk(¶ms, &CircuitVanilla::empty(circuit_version)).unwrap() + }; VerifyingKey { params, @@ -1127,10 +526,15 @@ impl ProvingKey { /// See [`OrchardCircuitVersion`] for which version to use. pub fn build(circuit_version: OrchardCircuitVersion) -> Self { let params = halo2_proofs::poly::commitment::Params::new(K); - let circuit = Circuit::empty(circuit_version); - - let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); - let pk = plonk::keygen_pk(¶ms, vk, &circuit).unwrap(); + let pk = if circuit_version.is_zsa() { + let circuit = CircuitZsa::empty(); + let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); + plonk::keygen_pk(¶ms, vk, &circuit).unwrap() + } else { + let circuit = CircuitVanilla::empty(circuit_version); + let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); + plonk::keygen_pk(¶ms, vk, &circuit).unwrap() + }; ProvingKey { params, @@ -1152,6 +556,11 @@ impl ProvingKey { /// Public inputs to the Orchard Action circuit. /// +/// The `enable_zsa` field was introduced with the ZSA feature; it did not exist before. +/// In vanilla Orchard, `enable_zsa` is always false, so this method always appends a zero to the +/// instance vector. Since halo2_proofs pads instance values with zero, old proofs (without this +/// extra entry) and new proofs behave identically. +/// /// # Invariants /// /// Every `Instance` has a non-identity `rk`. @@ -1165,6 +574,7 @@ pub struct Instance { enable_spend: bool, enable_output: bool, cross_address_disabled: bool, + enable_zsa: bool, } impl Instance { @@ -1209,6 +619,7 @@ impl Instance { enable_spend: flags.spends_enabled(), enable_output: flags.outputs_enabled(), cross_address_disabled: !flags.cross_address_enabled(), + enable_zsa: flags.zsa_enabled(), }) } @@ -1252,8 +663,18 @@ impl Instance { self.cross_address_disabled } - fn to_halo2_instance(&self) -> [[vesta::Scalar; 10]; 1] { - let mut instance = [vesta::Scalar::zero(); 10]; + /// Returns whether zsa are enabled for this instance. + pub(crate) fn enable_zsa(&self) -> bool { + self.enable_zsa + } + + /// Note: Before the ZSA feature was introduced, this method returned a 10-element instance slice. + /// With ZSA, it now returns 11 elements, the last one corresponding to `enable_zsa`. + /// In vanilla Orchard, `enable_zsa` is always false, so this extra element is always zero. + /// Since halo2_proofs pads instance values with zero, old proofs (without this element) + /// and new proofs behave identically. + fn to_halo2_instance(&self) -> [[vesta::Scalar; 11]; 1] { + let mut instance = [vesta::Scalar::zero(); 11]; instance[ANCHOR] = self.anchor.inner(); instance[CV_NET_X] = self.cv_net.x(); @@ -1278,6 +699,7 @@ impl Instance { // `Proof::verify`). instance[DISABLE_CROSS_ADDRESS] = vesta::Scalar::from(u64::from(self.cross_address_disabled)); + instance[ENABLE_ZSA] = vesta::Scalar::from(u64::from(self.enable_zsa)); [instance] } @@ -1294,7 +716,11 @@ impl Proof { /// /// Returns [`plonk::Error::InvalidInstances`] if any instance has /// `disableCrossAddress = 1` and `pk` is not an - /// [`OrchardCircuitVersion::PostNu6_3`] proving key. + /// [`OrchardCircuitVersion::PostNu6_3`] or an [`OrchardCircuitVersion::ZSA`] proving key. + /// + /// Returns [`plonk::Error::InvalidInstances`] if any instance has + /// `enable_zsa = 1` and `pk` is not an + /// [`OrchardCircuitVersion::ZSA`] proving key. /// /// All instances of a bundle carry the same `disableCrossAddress` value; that uniformity /// is the bundle layer's invariant, and is not checked here. @@ -1306,16 +732,21 @@ impl Proof { ) -> Result { if circuits .iter() - .any(|c| c.circuit_version != pk.circuit_version) + .any(|c| c.circuit_version() != pk.circuit_version) { return Err(plonk::Error::Synthesis); } + if instances.iter().any(Instance::cross_address_disabled) && !pk.supports_cross_address_restriction() { return Err(plonk::Error::InvalidInstances); } + if instances.iter().any(Instance::enable_zsa) && !pk.circuit_version.is_zsa() { + return Err(plonk::Error::InvalidInstances); + } + let instances: Vec<_> = instances.iter().map(|i| i.to_halo2_instance()).collect(); let instances: Vec> = instances .iter() @@ -1324,14 +755,32 @@ impl Proof { let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect(); let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]); - plonk::create_proof( - &pk.params, - &pk.pk, - circuits, - &instances, - &mut rng, - &mut transcript, - )?; + + if pk.circuit_version.is_zsa() { + let circuits: Vec<_> = circuits.iter().map(Circuit::to_zsa).collect(); + plonk::create_proof( + &pk.params, + &pk.pk, + &circuits, + &instances, + &mut rng, + &mut transcript, + )?; + } else { + let circuits: Vec<_> = circuits + .iter() + .map(Circuit::to_vanilla) + .collect::>()?; + plonk::create_proof( + &pk.params, + &pk.pk, + &circuits, + &instances, + &mut rng, + &mut transcript, + )?; + }; + Ok(Proof(transcript.finalize())) } @@ -1341,7 +790,11 @@ impl Proof { /// /// Returns [`plonk::Error::InvalidInstances`] if any instance has /// `disableCrossAddress = 1` and `vk` is not an - /// [`OrchardCircuitVersion::PostNu6_3`] verifying key. + /// [`OrchardCircuitVersion::PostNu6_3`] or an [`OrchardCircuitVersion::ZSA`] verifying key. + /// + /// Returns [`plonk::Error::InvalidInstances`] if any instance has + /// `enable_zsa = 1` and `vk` is not an + /// [`OrchardCircuitVersion::ZSA`] verifying key. /// /// Also returns an error if proof verification fails. pub fn verify(&self, vk: &VerifyingKey, instances: &[Instance]) -> Result<(), plonk::Error> { @@ -1351,6 +804,10 @@ impl Proof { return Err(plonk::Error::InvalidInstances); } + if instances.iter().any(Instance::enable_zsa) && !vk.circuit_version.is_zsa() { + return Err(plonk::Error::InvalidInstances); + } + let instances: Vec<_> = instances.iter().map(|i| i.to_halo2_instance()).collect(); let instances: Vec> = instances .iter() @@ -1400,661 +857,6 @@ mod fingerprint; #[cfg(test)] mod tests { - use alloc::vec::Vec; - use core::iter; - - use ff::Field; - use halo2_proofs::{circuit::Value, dev::MockProver}; - use pasta_curves::{pallas, vesta}; - use rand::{rngs::OsRng, RngCore}; - - use super::{Circuit, Instance, OrchardCircuitVersion, Proof, ProvingKey, VerifyingKey, K}; - use crate::{ - bundle::{BundleVersion, Flags}, - keys::SpendValidatingKey, - note::{Note, NoteVersion, Rho}, - tree::MerklePath, - value::{ValueCommitTrapdoor, ValueCommitment}, - }; - - /// Generates a circuit and instance whose output note is addressed to an expanded - /// receiver distinct from the spent note's. - fn generate_circuit_instance( - rng: R, - circuit_version: OrchardCircuitVersion, - ) -> (Circuit, Instance) { - generate_circuit_instance_inner(rng, circuit_version, false) - } - - /// Generates a circuit and instance whose output note is addressed to the spent - /// note's expanded receiver, as the cross-address restriction requires. - fn generate_self_transfer_circuit_instance( - rng: R, - circuit_version: OrchardCircuitVersion, - ) -> (Circuit, Instance) { - generate_circuit_instance_inner(rng, circuit_version, true) - } - - fn generate_circuit_instance_inner( - mut rng: R, - circuit_version: OrchardCircuitVersion, - output_matches_spend: bool, - ) -> (Circuit, Instance) { - // Note Version does not matter for this - let note_version = NoteVersion::V2; - let (_, fvk, spent_note) = Note::dummy(&mut rng, None, note_version); - - let sender_address = spent_note.recipient(); - let nk = *fvk.nk(); - let rivk = fvk.rivk(fvk.scope_for_address(&spent_note.recipient()).unwrap()); - let nf_old = spent_note.nullifier(&fvk); - let rho = Rho::from_nf_old(nf_old); - let ak: SpendValidatingKey = fvk.into(); - let alpha = pallas::Scalar::random(&mut rng); - let rk = ak.randomize(&alpha); - - let output_note = if output_matches_spend { - Note::new( - sender_address, - spent_note.value(), - rho, - note_version, - &mut rng, - ) - } else { - loop { - let (_, _, output_note) = Note::dummy(&mut rng, Some(rho), note_version); - if !sender_address.same_expanded_receiver(&output_note.recipient()) { - break output_note; - } - } - }; - let cmx = output_note.commitment().into(); - - let value = spent_note.value() - output_note.value(); - let rcv = ValueCommitTrapdoor::random(&mut rng); - let cv_net = ValueCommitment::derive(value, rcv.clone()); - - let path = MerklePath::dummy(&mut rng); - let anchor = path.root(spent_note.commitment().into()); - - ( - Circuit { - circuit_version, - path: Value::known(path.auth_path()), - pos: Value::known(path.position()), - g_d_old: Value::known(sender_address.g_d()), - pk_d_old: Value::known(*sender_address.pk_d()), - v_old: Value::known(spent_note.value()), - rho_old: Value::known(spent_note.rho()), - psi_old: Value::known(spent_note.psi()), - rcm_old: Value::known(spent_note.rcm()), - cm_old: Value::known(spent_note.commitment()), - alpha: Value::known(alpha), - ak: Value::known(ak), - nk: Value::known(nk), - rivk: Value::known(rivk), - g_d_new: Value::known(output_note.recipient().g_d()), - pk_d_new: Value::known(*output_note.recipient().pk_d()), - v_new: Value::known(output_note.value()), - psi_new: Value::known(output_note.psi()), - rcm_new: Value::known(output_note.rcm()), - rcv: Value::known(rcv), - }, - Instance { - anchor, - cv_net, - nf_old, - rk, - cmx, - enable_spend: true, - enable_output: true, - cross_address_disabled: false, - }, - ) - } - - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - enum ProofFixtureEncoding { - LegacyTwoFlags, - PostNu6_3ThreeFlags, - } - - fn write_test_case( - mut w: W, - instance: &Instance, - proof: &Proof, - encoding: ProofFixtureEncoding, - ) -> std::io::Result<()> { - w.write_all(&instance.anchor().to_bytes())?; - w.write_all(&instance.cv_net().to_bytes())?; - w.write_all(&instance.nf_old().to_bytes())?; - w.write_all(&<[u8; 32]>::from(instance.rk()))?; - w.write_all(&instance.cmx().to_bytes())?; - match encoding { - ProofFixtureEncoding::LegacyTwoFlags => { - w.write_all(&[ - u8::from(instance.enable_spend()), - u8::from(instance.enable_output()), - ])?; - } - ProofFixtureEncoding::PostNu6_3ThreeFlags => { - w.write_all(&[ - u8::from(instance.enable_spend()), - u8::from(instance.enable_output()), - u8::from(instance.cross_address_disabled()), - ])?; - } - } - w.write_all(proof.as_ref())?; - Ok(()) - } - - fn read_test_case( - mut r: R, - encoding: ProofFixtureEncoding, - ) -> std::io::Result<(Instance, Proof)> { - let read_32_bytes = |r: &mut R| { - let mut ret = [0u8; 32]; - r.read_exact(&mut ret).unwrap(); - ret - }; - let read_bool = |r: &mut R| { - let mut byte = [0u8; 1]; - r.read_exact(&mut byte).unwrap(); - match byte { - [0] => false, - [1] => true, - _ => panic!("Unexpected non-boolean byte"), - } - }; - - let anchor = crate::Anchor::from_bytes(read_32_bytes(&mut r)).unwrap(); - let cv_net = ValueCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap(); - let nf_old = crate::note::Nullifier::from_bytes(&read_32_bytes(&mut r)).unwrap(); - let rk = read_32_bytes(&mut r).try_into().unwrap(); - let cmx = crate::note::ExtractedNoteCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap(); - let enable_spend = read_bool(&mut r); - let enable_output = read_bool(&mut r); - let (cross_address_bit, bundle_version) = match encoding { - ProofFixtureEncoding::LegacyTwoFlags => (0, BundleVersion::orchard_v2()), - ProofFixtureEncoding::PostNu6_3ThreeFlags => { - // The fixture stores the instance-level *disable* bit; the NU6.3 flag - // byte carries the *enable* bit, so invert when reconstructing. - // - // The circuit is pool-agnostic; decode under Ironwood, the pool whose flag - // byte can represent `enableCrossAddress` either way (Orchard post-NU6.3 - // rejects bit 2). - let cross_address_disabled = read_bool(&mut r); - ( - u8::from(!cross_address_disabled) << 2, - BundleVersion::ironwood_v3(), - ) - } - }; - let flags = Flags::from_byte( - u8::from(enable_spend) | (u8::from(enable_output) << 1) | cross_address_bit, - bundle_version, - ) - .expect("test vectors use canonical flag encodings"); - - let instance = Instance::from_parts(anchor, cv_net, nf_old, rk, cmx, flags) - .expect("test vectors were generated with non-identity rk"); - - let mut proof_bytes = vec![]; - r.read_to_end(&mut proof_bytes)?; - let proof = Proof::new(proof_bytes); - - Ok((instance, proof)) - } - - #[test] - fn halo2_instance_includes_cross_address_disabled_flag() { - let (_, mut instance) = - generate_circuit_instance(OsRng, OrchardCircuitVersion::FixedPostNu6_2); - - let halo2_instance = instance.to_halo2_instance(); - assert_eq!(halo2_instance[0].len(), 10); - assert_eq!( - halo2_instance[0][super::DISABLE_CROSS_ADDRESS], - vesta::Scalar::zero() - ); - - instance.cross_address_disabled = true; - assert_eq!( - instance.to_halo2_instance()[0][super::DISABLE_CROSS_ADDRESS], - vesta::Scalar::one() - ); - } - - #[test] - fn cross_address_support_matches_circuit_version() { - assert!(!OrchardCircuitVersion::InsecurePreNu6_2.supports_cross_address_restriction()); - assert!(!OrchardCircuitVersion::FixedPostNu6_2.supports_cross_address_restriction()); - assert!(OrchardCircuitVersion::PostNu6_3.supports_cross_address_restriction()); - } - - #[test] - fn post_nu6_3_cross_address_restriction_is_conditional() { - let mock_verify = |circuit: &Circuit, instance: &Instance| { - MockProver::run( - K, - circuit, - instance - .to_halo2_instance() - .iter() - .map(|p| p.to_vec()) - .collect(), - ) - .unwrap() - .verify() - }; - - // An unrestricted cross-address statement is satisfiable... - let (circuit, mut instance) = - generate_circuit_instance(OsRng, OrchardCircuitVersion::PostNu6_3); - assert_eq!(mock_verify(&circuit, &instance), Ok(())); - - // ...but setting `disableCrossAddress` makes it unsatisfiable... - instance.cross_address_disabled = true; - assert!(mock_verify(&circuit, &instance).is_err()); - - // ...while a restricted self-transfer statement is satisfiable. - let (circuit, mut instance) = - generate_self_transfer_circuit_instance(OsRng, OrchardCircuitVersion::PostNu6_3); - instance.cross_address_disabled = true; - assert_eq!(mock_verify(&circuit, &instance), Ok(())); - } - - #[test] - fn post_nu6_3_restricted_statement_proves_and_verifies() { - let mut rng = OsRng; - let (circuit, mut instance) = - generate_self_transfer_circuit_instance(&mut rng, OrchardCircuitVersion::PostNu6_3); - instance.cross_address_disabled = true; - - let pk = ProvingKey::build(OrchardCircuitVersion::PostNu6_3); - let vk = VerifyingKey::build(OrchardCircuitVersion::PostNu6_3); - - let proof = Proof::create( - &pk, - core::slice::from_ref(&circuit), - core::slice::from_ref(&instance), - &mut rng, - ) - .unwrap(); - assert!(proof.verify(&vk, core::slice::from_ref(&instance)).is_ok()); - } - - // FixedPostNu6_2 leaves instance row 9 (`disableCrossAddress`) unconstrained, so a - // freshly created proof can satisfy a restricted statement at the raw halo2 level - // without enforcing anything about addresses. This test documents that hazard and - // pins the API checks that close it. - #[test] - fn restricted_statement_requires_supporting_key() { - use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite}; - - let mut rng = OsRng; - let (circuit, mut instance) = - generate_circuit_instance(&mut rng, OrchardCircuitVersion::FixedPostNu6_2); - instance.cross_address_disabled = true; - - let pk = ProvingKey::build(OrchardCircuitVersion::FixedPostNu6_2); - let vk = VerifyingKey::build(OrchardCircuitVersion::FixedPostNu6_2); - - let raw_instances = instance.to_halo2_instance(); - let raw_instances: Vec<_> = raw_instances.iter().map(|i| &i[..]).collect(); - let raw_instances = [&raw_instances[..]]; - - let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]); - super::plonk::create_proof( - &pk.params, - &pk.pk, - core::slice::from_ref(&circuit), - &raw_instances, - &mut rng, - &mut transcript, - ) - .unwrap(); - let proof_bytes = transcript.finalize(); - - let strategy = super::SingleVerifier::new(&vk.params); - let mut transcript = Blake2bRead::init(&proof_bytes[..]); - assert!(super::plonk::verify_proof( - &vk.params, - &vk.vk, - strategy, - &raw_instances, - &mut transcript, - ) - .is_ok()); - - assert!(matches!( - Proof::create( - &pk, - core::slice::from_ref(&circuit), - core::slice::from_ref(&instance), - &mut rng, - ), - Err(super::plonk::Error::InvalidInstances), - )); - - let proof = Proof::new(proof_bytes); - assert!(matches!( - proof.verify(&vk, core::slice::from_ref(&instance)), - Err(super::plonk::Error::InvalidInstances), - )); - } - - // Set ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF to regenerate the pinned circuit description - // for this version. - fn pinned_circuit_description( - circuit_version: OrchardCircuitVersion, - path: &str, - expected: &str, - ) -> VerifyingKey { - let vk = VerifyingKey::build(circuit_version); - - if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() { - std::fs::write(path, format!("{:#?}\n", vk.vk.pinned())) - .expect("should be able to write new circuit description"); - } else { - assert_eq!( - format!("{:#?}\n", vk.vk.pinned()), - expected.replace("\r\n", "\n") - ); - } - - vk - } - - // TODO: recast as a proptest - fn round_trip_for_version(circuit_version: OrchardCircuitVersion, vk: &VerifyingKey) { - let mut rng = OsRng; - - let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) - .map(|()| generate_circuit_instance(&mut rng, circuit_version)) - .unzip(); - - // Test that the proof size is as expected. - let expected_proof_size = { - let circuit_cost = - halo2_proofs::dev::CircuitCost::::measure( - K, - &circuits[0], - ); - // These sizes are identical for every circuit version: the post-NU 6.3 circuit reuses the - // existing Orchard checks gate on spare rows and adds no columns or - // commitments, leaving the proof shape unchanged. - assert_eq!(usize::from(circuit_cost.proof_size(1)), 4992); - assert_eq!(usize::from(circuit_cost.proof_size(2)), 7264); - // The constants in `Proof::expected_proof_size` must track the circuit's actual - // proof size; this guards them against drift if the circuit ever changes. - assert_eq!(Proof::expected_proof_size(1), 4992); - assert_eq!(Proof::expected_proof_size(2), 7264); - assert_eq!( - Proof::expected_proof_size(instances.len()), - usize::from(circuit_cost.proof_size(instances.len())), - ); - usize::from(circuit_cost.proof_size(instances.len())) - }; - - for (circuit, instance) in circuits.iter().zip(instances.iter()) { - assert_eq!( - MockProver::run( - K, - circuit, - instance - .to_halo2_instance() - .iter() - .map(|p| p.to_vec()) - .collect() - ) - .unwrap() - .verify(), - Ok(()) - ); - } - - let pk = ProvingKey::build(circuit_version); - let proof = Proof::create(&pk, &circuits, &instances, &mut rng).unwrap(); - assert!(proof.verify(vk, &instances).is_ok()); - assert_eq!(proof.0.len(), expected_proof_size); - } - - #[test] - fn round_trip_fixed() { - let vk = pinned_circuit_description( - OrchardCircuitVersion::FixedPostNu6_2, - "src/circuit_data/circuit_description_fixed", - include_str!("circuit_data/circuit_description_fixed"), - ); - round_trip_for_version(OrchardCircuitVersion::FixedPostNu6_2, &vk); - } - - #[test] - fn round_trip_post_nu6_3() { - let vk = pinned_circuit_description( - OrchardCircuitVersion::PostNu6_3, - "src/circuit_data/circuit_description_post_nu6_3", - include_str!("circuit_data/circuit_description_post_nu6_3"), - ); - round_trip_for_version(OrchardCircuitVersion::PostNu6_3, &vk); - } - - // Proves with the proving key for `proving_version` and checks that the proof verifies under - // the verifying key for the same version, but not under a version with a different verifying - // key. - fn proof_is_rejected_by_other_circuit_version( - proving_version: OrchardCircuitVersion, - other_version: OrchardCircuitVersion, - ) { - let mut rng = OsRng; - - let (circuit, instance) = generate_circuit_instance(&mut rng, proving_version); - let instances = core::slice::from_ref(&instance); - - let pk = ProvingKey::build(proving_version); - let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); - - // Verifies under the matching version's verifying key. - let vk_matching = VerifyingKey::build(proving_version); - assert!(proof.verify(&vk_matching, instances).is_ok()); - - // Does not verify under the other version's verifying key. - let vk_other = VerifyingKey::build(other_version); - assert!(proof.verify(&vk_other, instances).is_err()); - } - - #[test] - fn proof_verifies_against_matching_circuit_version() { - // Insecure proofs are rejected by the anchored circuit versions, and anchored proofs are - // rejected by the insecure verifying key. - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::FixedPostNu6_2, - OrchardCircuitVersion::InsecurePreNu6_2, - ); - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::PostNu6_3, - OrchardCircuitVersion::InsecurePreNu6_2, - ); - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::InsecurePreNu6_2, - OrchardCircuitVersion::FixedPostNu6_2, - ); - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::InsecurePreNu6_2, - OrchardCircuitVersion::PostNu6_3, - ); - } - - #[test] - fn fixed_and_post_nu6_3_have_distinct_verifying_keys() { - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::FixedPostNu6_2, - OrchardCircuitVersion::PostNu6_3, - ); - proof_is_rejected_by_other_circuit_version( - OrchardCircuitVersion::PostNu6_3, - OrchardCircuitVersion::FixedPostNu6_2, - ); - } - - // Proving a circuit with a proving key for a different circuit version is a misuse: the - // proving key and circuits must agree (see `Proof::create`). Confirm `create` rejects it - // with `plonk::Error::Synthesis` rather than emitting an unverifiable proof. - #[test] - fn create_rejects_mismatched_proving_key_version() { - let mut rng = OsRng; - - for (circuit_version, pk_version) in [ - ( - OrchardCircuitVersion::InsecurePreNu6_2, - OrchardCircuitVersion::FixedPostNu6_2, - ), - ( - OrchardCircuitVersion::FixedPostNu6_2, - OrchardCircuitVersion::PostNu6_3, - ), - ( - OrchardCircuitVersion::PostNu6_3, - OrchardCircuitVersion::FixedPostNu6_2, - ), - ] { - let (circuit, instance) = generate_circuit_instance(&mut rng, circuit_version); - let instances = core::slice::from_ref(&instance); - - let mismatched_pk = ProvingKey::build(pk_version); - - assert!(matches!( - Proof::create(&mismatched_pk, &[circuit], instances, &mut rng), - Err(super::plonk::Error::Synthesis), - )); - } - } - - fn serialized_proof_test_case_for_version( - circuit_version: OrchardCircuitVersion, - proof_path: &str, - test_case_bytes: &[u8], - encoding: ProofFixtureEncoding, - expected_proof_size: usize, - restricted: bool, - ) { - let vk = VerifyingKey::build(circuit_version); - // Set ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF to regenerate this serialized proof - // fixture. The non-regeneration path embeds and verifies the checked-in fixture. - if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() { - let create_proof = || -> std::io::Result<()> { - let mut rng = OsRng; - - let (circuit, mut instance) = if restricted { - generate_self_transfer_circuit_instance(&mut rng, circuit_version) - } else { - generate_circuit_instance(&mut rng, circuit_version) - }; - instance.cross_address_disabled = restricted; - let instances = core::slice::from_ref(&instance); - - let pk = ProvingKey::build(circuit_version); - let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); - assert!(proof.verify(&vk, instances).is_ok()); - - let file = std::fs::File::create(proof_path)?; - write_test_case(file, &instance, &proof, encoding) - }; - create_proof().expect("should be able to write new proof"); - // Regeneration only writes the fixture; the non-generate run below embeds and - // verifies it. - return; - } - - // Parse the hardcoded proof test case. - let (instance, proof) = - read_test_case(test_case_bytes, encoding).expect("proof must be valid"); - assert_eq!(instance.cross_address_disabled(), restricted); - assert_eq!(proof.0.len(), expected_proof_size); - - assert!(proof.verify(&vk, &[instance]).is_ok()); - } - - #[test] - fn serialized_fixed_proof_test_case() { - serialized_proof_test_case_for_version( - OrchardCircuitVersion::FixedPostNu6_2, - "src/circuit_data/circuit_proof_test_case_fixed.bin", - include_bytes!("circuit_data/circuit_proof_test_case_fixed.bin"), - ProofFixtureEncoding::LegacyTwoFlags, - 4992, - false, - ); - } - - #[test] - fn serialized_post_nu6_3_proof_test_case() { - serialized_proof_test_case_for_version( - OrchardCircuitVersion::PostNu6_3, - "src/circuit_data/circuit_proof_test_case_post_nu6_3.bin", - include_bytes!("circuit_data/circuit_proof_test_case_post_nu6_3.bin"), - ProofFixtureEncoding::PostNu6_3ThreeFlags, - 4992, - false, - ); - } - - #[test] - fn serialized_post_nu6_3_restricted_proof_test_case() { - serialized_proof_test_case_for_version( - OrchardCircuitVersion::PostNu6_3, - "src/circuit_data/circuit_proof_test_case_post_nu6_3_restricted.bin", - include_bytes!("circuit_data/circuit_proof_test_case_post_nu6_3_restricted.bin"), - ProofFixtureEncoding::PostNu6_3ThreeFlags, - 4992, - true, - ); - } - - // The deployed (NU5..NU6.2) verifying key and a pre-fix proof. `InsecurePreNu6_2` - // reconstructs the historical circuit, so this checks that the deployed verifying key is - // reproduced exactly and that the old proof still verifies under it — the guarantee that - // lets a node sync from before the fix. These fixtures are frozen as the canonical - // pre-NU6.2 verifying key and a sample proof, so they are never regenerated. - #[test] - fn insecure_against_stored_circuit() { - let vk = VerifyingKey::build(OrchardCircuitVersion::InsecurePreNu6_2); - assert_eq!( - format!("{:#?}\n", vk.vk.pinned()), - include_str!("circuit_data/circuit_description_insecure").replace("\r\n", "\n") - ); - - let (instance, proof) = { - let test_case_bytes = - include_bytes!("circuit_data/circuit_proof_test_case_insecure.bin"); - read_test_case(&test_case_bytes[..], ProofFixtureEncoding::LegacyTwoFlags) - .expect("proof must be valid") - }; - assert_eq!(proof.0.len(), 4992); - assert!(proof.verify(&vk, &[instance]).is_ok()); - } - - #[cfg(feature = "dev-graph")] - #[test] - fn print_action_circuit() { - use plotters::prelude::*; - - let root = BitMapBackend::new("action-circuit-layout.png", (1024, 768)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root - .titled("Orchard Action Circuit", ("sans-serif", 60)) - .unwrap(); - - let circuit = Circuit::empty(OrchardCircuitVersion::FixedPostNu6_2); - halo2_proofs::dev::CircuitLayout::default() - .show_labels(false) - .view_height(0..(1 << 11)) - .render(K, &circuit, &root) - .unwrap(); - } mod from_parts_rk_identity { use ff::{Field as _, PrimeField as _}; @@ -2112,4 +914,43 @@ mod tests { assert_eq!(instance.rk(), &rk); } } + + mod to_vanilla_zsa_witnesses_invariant { + use ff::Field; + use halo2_proofs::circuit::Value; + use pasta_curves::pallas; + + use super::super::{ + plonk, AdditionalZsaWitnesses, Circuit, CircuitVanilla, OrchardCircuitVersion, + }; + use crate::note::AssetBase; + + /// `Circuit::to_vanilla` must reject any `Circuit` that carries known ZSA-specific + /// witnesses for a non-ZSA `circuit_version`: the Vanilla circuit versions have no + /// way to prove them, so their presence indicates a construction bug rather than a + /// provable statement. + #[test] + fn errors_if_zsa_witnesses_are_known() { + let circuit = Circuit { + common_witnesses: CircuitVanilla::empty(OrchardCircuitVersion::FixedPostNu6_2), + additional_zsa_witnesses: Value::known(AdditionalZsaWitnesses { + psi_nf: pallas::Base::ZERO, + asset: AssetBase::zatoshi(), + split_flag: false, + }), + }; + + assert!(matches!(circuit.to_vanilla(), Err(plonk::Error::Synthesis))); + } + + #[test] + fn accepts_unknown_zsa_witnesses() { + let circuit = Circuit { + common_witnesses: CircuitVanilla::empty(OrchardCircuitVersion::FixedPostNu6_2), + additional_zsa_witnesses: Value::unknown(), + }; + + assert!(circuit.to_vanilla().is_ok()); + } + } } diff --git a/src/circuit/circuit_vanilla.rs b/src/circuit/circuit_vanilla.rs new file mode 100644 index 000000000..fd7fea0ac --- /dev/null +++ b/src/circuit/circuit_vanilla.rs @@ -0,0 +1,1416 @@ +//! The Orchard Action circuit implementation for the Vanilla variation of the Orchard protocol. +//! +//! Includes the configuration, synthesis, and proof verification logic. + +use group::Curve; + +use pasta_curves::pallas; + +use halo2_gadgets::{ + ecc::{FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarVar}, + sinsemilla::{chip::SinsemillaChip, merkle::MerklePath}, + utilities::lookup_range_check::PallasLookupRangeCheckConfig, +}; +use halo2_proofs::{ + circuit::{floor_planner, Layouter, Value}, + plonk::{self, Advice, Column, Constraints, Expression, Selector}, + poly::Rotation, +}; + +use crate::{ + circuit::{ + commit_ivk::gadgets::commit_ivk, configure_circuit, + derive_nullifier::gadgets::derive_nullifier, gadget::assign_free_advice, + note_commit::gadgets::note_commit, value_commit_orchard::gadgets::value_commit_orchard, + AddressPoints, Config, OrchardCircuitVersion, ANCHOR, CMX, CV_NET_X, CV_NET_Y, + DISABLE_CROSS_ADDRESS, ENABLE_OUTPUT, ENABLE_SPEND, NF_OLD, RK_X, RK_Y, + }, + constants::{OrchardFixedBasesFull, OrchardHashDomains, MERKLE_DEPTH_ORCHARD}, + keys::{ + CommitIvkRandomness, DiversifiedTransmissionKey, NullifierDerivingKey, SpendValidatingKey, + }, + note::{ + commitment::{NoteCommitTrapdoor, NoteCommitment}, + Rho, + }, + spec::NonIdentityPallasPoint, + tree::MerkleHashOrchard, + value::{NoteValue, ValueCommitTrapdoor}, +}; + +/// The OrchardVanilla Action circuit. +/// +/// This structure is embedded in `CircuitZsa` for the ZSA variation. +#[derive(Clone, Debug)] +pub struct CircuitVanilla { + pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, + pub(crate) pos: Value, + pub(crate) g_d_old: Value, + pub(crate) pk_d_old: Value, + pub(crate) v_old: Value, + pub(crate) rho_old: Value, + pub(crate) psi_old: Value, + pub(crate) rcm_old: Value, + pub(crate) cm_old: Value, + pub(crate) alpha: Value, + pub(crate) ak: Value, + pub(crate) nk: Value, + pub(crate) rivk: Value, + pub(crate) g_d_new: Value, + pub(crate) pk_d_new: Value, + pub(crate) v_new: Value, + pub(crate) psi_new: Value, + pub(crate) rcm_new: Value, + pub(crate) rcv: Value, + + pub(crate) circuit_version: OrchardCircuitVersion, +} + +impl CircuitVanilla { + /// Returns an empty circuit with all private witnesses unknown. + /// + /// This is used for circuit shape-dependent operations, such as generating keys + /// or rendering the circuit layout, where witness values are not required but the + /// selected circuit version still determines the configured constraints. + pub(crate) fn empty(circuit_version: OrchardCircuitVersion) -> Self { + CircuitVanilla { + path: Value::unknown(), + pos: Value::unknown(), + g_d_old: Value::unknown(), + pk_d_old: Value::unknown(), + v_old: Value::unknown(), + rho_old: Value::unknown(), + psi_old: Value::unknown(), + rcm_old: Value::unknown(), + cm_old: Value::unknown(), + alpha: Value::unknown(), + ak: Value::unknown(), + nk: Value::unknown(), + rivk: Value::unknown(), + g_d_new: Value::unknown(), + pk_d_new: Value::unknown(), + v_new: Value::unknown(), + psi_new: Value::unknown(), + rcm_new: Value::unknown(), + rcv: Value::unknown(), + circuit_version, + } + } +} + +/// Creates the `q_orchard` gate checking the OrchardVanilla Action statement, on the +/// given selector. +pub(super) fn configure_vanilla_orchard_gate( + meta: &mut plonk::ConstraintSystem, + advices: [Column; 10], + q_orchard: Selector, +) { + // Constrain v_old - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). + // Either v_old = 0, or calculated root = anchor (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + // Constrain v_old = 0 or enable_spend = 1 (https://p.z.cash/ZKS:action-enable-spend). + // Constrain v_new = 0 or enable_output = 1 (https://p.z.cash/ZKS:action-enable-output). + // + // This gate is also reused for the same-address check; see + // [`CircuitVanilla::synthesize_cross_address_checks`]. + meta.create_gate("Orchard circuit checks", |meta| { + let q_orchard = meta.query_selector(q_orchard); + let v_old = meta.query_advice(advices[0], Rotation::cur()); + let v_new = meta.query_advice(advices[1], Rotation::cur()); + let magnitude = meta.query_advice(advices[2], Rotation::cur()); + let sign = meta.query_advice(advices[3], Rotation::cur()); + + let root = meta.query_advice(advices[4], Rotation::cur()); + let anchor = meta.query_advice(advices[5], Rotation::cur()); + + let enable_spend = meta.query_advice(advices[6], Rotation::cur()); + let enable_output = meta.query_advice(advices[7], Rotation::cur()); + + let one = Expression::Constant(pallas::Base::one()); + + Constraints::with_selector( + q_orchard, + [ + ( + "v_old - v_new = magnitude * sign", + v_old.clone() - v_new.clone() - magnitude * sign, + ), + ( + "Either v_old = 0, or root = anchor", + v_old.clone() * (root - anchor), + ), + ( + "v_old = 0 or enable_spend = 1", + v_old * (one.clone() - enable_spend), + ), + ( + "v_new = 0 or enable_output = 1", + v_new * (one - enable_output), + ), + ], + ) + }); +} + +impl plonk::Circuit for CircuitVanilla { + type Config = Config; + type FloorPlanner = floor_planner::V1; + + fn without_witnesses(&self) -> Self { + CircuitVanilla::empty(self.circuit_version) + } + + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { + configure_circuit(meta, false) + } + + #[allow(non_snake_case)] + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), plonk::Error> { + if self.circuit_version.is_zsa() { + return Err(plonk::Error::Synthesis); + } + + let addrs = self.synthesize_base(&config, &mut layouter)?; + + if self.circuit_version.supports_cross_address_restriction() { + CircuitVanilla::synthesize_cross_address_checks(&config, &mut layouter, &addrs)?; + } + + Ok(()) + } +} + +impl CircuitVanilla { + /// Synthesizes the Orchard Action checks common to every circuit version, + /// parameterized by `self.circuit_version`, returning the cells carrying the old + /// and new note addresses so that circuit versions can impose additional + /// constraints on them. + #[allow(non_snake_case)] + fn synthesize_base( + &self, + config: &Config, + layouter: &mut impl Layouter, + ) -> Result, plonk::Error> { + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::load(config.sinsemilla_config_1.clone(), layouter)?; + + // Construct the ECC chip. + let ecc_chip = config.ecc_chip(self.circuit_version.halo2_version()); + + // Witness private inputs that are used across multiple checks. + let (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) = { + // Witness psi_old + let psi_old = assign_free_advice( + layouter.namespace(|| "witness psi_old"), + config.advices[0], + self.psi_old, + )?; + + // Witness rho_old + let rho_old = assign_free_advice( + layouter.namespace(|| "witness rho_old"), + config.advices[0], + self.rho_old.map(|rho| rho.into_inner()), + )?; + + // Witness cm_old + let cm_old = Point::new( + ecc_chip.clone(), + layouter.namespace(|| "cm_old"), + self.cm_old.as_ref().map(|cm| cm.inner().to_affine()), + )?; + + // Witness g_d_old + let g_d_old = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "gd_old"), + self.g_d_old.as_ref().map(|gd| gd.to_affine()), + )?; + + // Witness ak_P. + let ak_P: Value = self.ak.as_ref().map(|ak| ak.into()); + let ak_P = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness ak_P"), + ak_P.map(|ak_P| ak_P.to_affine()), + )?; + + // Witness nk. + let nk = assign_free_advice( + layouter.namespace(|| "witness nk"), + config.advices[0], + self.nk.map(|nk| nk.inner()), + )?; + + // Witness v_old. + let v_old = assign_free_advice( + layouter.namespace(|| "witness v_old"), + config.advices[0], + self.v_old, + )?; + + // Witness v_new. + let v_new = assign_free_advice( + layouter.namespace(|| "witness v_new"), + config.advices[0], + self.v_new, + )?; + + (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) + }; + + // Merkle path validity check (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + let root = { + let path = self + .path + .map(|typed_path| typed_path.map(|node| node.inner())); + let merkle_inputs = MerklePath::construct( + [config.merkle_chip_1(), config.merkle_chip_2()], + OrchardHashDomains::MerkleCrh, + self.pos, + path, + ); + let leaf = cm_old.extract_p().inner().clone(); + merkle_inputs.calculate_root(layouter.namespace(|| "Merkle path"), leaf)? + }; + + // Value commitment integrity (https://p.z.cash/ZKS:action-cv-net-integrity?partial). + let v_net_magnitude_sign = { + // Witness the magnitude and sign of v_net = v_old - v_new + let v_net_magnitude_sign = { + let v_net = self.v_old - self.v_new; + let magnitude_sign = v_net.map(|v_net| { + let (magnitude, sign) = v_net.magnitude_sign(); + + ( + // magnitude is guaranteed to be an unsigned 64-bit value. + // Therefore, we can move it into the base field. + pallas::Base::from(magnitude), + match sign { + crate::value::Sign::Positive => pallas::Base::one(), + crate::value::Sign::Negative => -pallas::Base::one(), + }, + ) + }); + + let magnitude = assign_free_advice( + layouter.namespace(|| "v_net magnitude"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.0), + )?; + let sign = assign_free_advice( + layouter.namespace(|| "v_net sign"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.1), + )?; + (magnitude, sign) + }; + + let rcv = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcv"), + self.rcv.as_ref().map(|rcv| rcv.inner()), + )?; + + let cv_net = value_commit_orchard( + layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net)"), + ecc_chip.clone(), + v_net_magnitude_sign.clone(), + rcv, + None, + )?; + + // Constrain cv_net to equal public input + layouter.constrain_instance(cv_net.inner().x().cell(), config.primary, CV_NET_X)?; + layouter.constrain_instance(cv_net.inner().y().cell(), config.primary, CV_NET_Y)?; + + // Return the magnitude and sign so we can use them in the Orchard gate. + v_net_magnitude_sign + }; + + // Nullifier integrity (https://p.z.cash/ZKS:action-nullifier-integrity). + let nf_old = { + let nf_old = derive_nullifier( + layouter.namespace(|| "nf_old = DeriveNullifier_nk(rho_old, psi_old, cm_old)"), + config.poseidon_chip(), + config.add_chip(), + ecc_chip.clone(), + rho_old.clone(), + &psi_old, + &cm_old, + nk.clone(), + None, + )?; + + // Constrain nf_old to equal public input + layouter.constrain_instance(nf_old.inner().cell(), config.primary, NF_OLD)?; + + nf_old + }; + + // Spend authority (https://p.z.cash/ZKS:action-spend-authority) + { + let alpha = + ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "alpha"), self.alpha)?; + + // alpha_commitment = [alpha] SpendAuthG + let (alpha_commitment, _) = { + let spend_auth_g = OrchardFixedBasesFull::SpendAuthG; + let spend_auth_g = FixedPoint::from_inner(ecc_chip.clone(), spend_auth_g); + spend_auth_g.mul(layouter.namespace(|| "[alpha] SpendAuthG"), alpha)? + }; + + // [alpha] SpendAuthG + ak_P + let rk = alpha_commitment.add(layouter.namespace(|| "rk"), &ak_P)?; + + // Constrain rk to equal public input + layouter.constrain_instance(rk.inner().x().cell(), config.primary, RK_X)?; + layouter.constrain_instance(rk.inner().y().cell(), config.primary, RK_Y)?; + } + + // Diversified address integrity (https://p.z.cash/ZKS:action-addr-integrity?partial). + let pk_d_old = { + let ivk = { + let ak = ak_P.extract_p().inner().clone(); + let rivk = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rivk"), + self.rivk.map(|rivk| rivk.inner()), + )?; + + commit_ivk( + config.sinsemilla_chip_1(), + ecc_chip.clone(), + config.commit_ivk_chip(), + layouter.namespace(|| "CommitIvk"), + ak, + nk, + rivk, + )? + }; + let ivk = + ScalarVar::from_base(ecc_chip.clone(), layouter.namespace(|| "ivk"), ivk.inner())?; + + // [ivk] g_d_old + // The scalar value is passed through and discarded. + let (derived_pk_d_old, _ivk) = + g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk)?; + + // Constrain derived pk_d_old to equal witnessed pk_d_old + // + // This equality constraint is technically superfluous, because the assigned + // value of `derived_pk_d_old` is an equivalent witness. But it's nice to see + // an explicit connection between circuit-synthesized values, and explicit + // prover witnesses. We could get the best of both worlds with a write-on-copy + // abstraction (https://github.com/zcash/halo2/issues/334). + let pk_d_old = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d_old"), + self.pk_d_old.map(|pk_d_old| pk_d_old.inner().to_affine()), + )?; + derived_pk_d_old + .constrain_equal(layouter.namespace(|| "pk_d_old equality"), &pk_d_old)?; + + pk_d_old + }; + + // Old note commitment integrity (https://p.z.cash/ZKS:action-cm-old-integrity?partial). + { + let rcm_old = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcm_old"), + self.rcm_old.as_ref().map(|rcm_old| rcm_old.inner()), + )?; + + // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) + let derived_cm_old = note_commit( + layouter.namespace(|| { + "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" + }), + config.sinsemilla_chip_1(), + config.ecc_chip(self.circuit_version.halo2_version()), + config.note_commit_chip_old(), + g_d_old.inner(), + pk_d_old.inner(), + v_old.clone(), + rho_old, + psi_old, + rcm_old, + None, + )?; + + // Constrain derived cm_old to equal witnessed cm_old + derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; + } + + // Witness g_d_new, used in the new note commitment integrity check below and, + // for the post-NU 6.3 circuit, in the cross-address checks. + let g_d_new = { + let g_d_new = self.g_d_new.map(|g_d_new| g_d_new.to_affine()); + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness g_d_new_star"), + g_d_new, + )? + }; + + // Witness pk_d_new, used in the new note commitment integrity check below and, + // for the post-NU 6.3 circuit, in the cross-address checks. + let pk_d_new = { + let pk_d_new = self.pk_d_new.map(|pk_d_new| pk_d_new.inner().to_affine()); + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d_new"), + pk_d_new, + )? + }; + + // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). + { + // ρ^new = nf^old + let rho_new = nf_old.inner().clone(); + + // Witness psi_new + let psi_new = assign_free_advice( + layouter.namespace(|| "witness psi_new"), + config.advices[0], + self.psi_new, + )?; + + let rcm_new = ScalarFixed::new( + ecc_chip, + layouter.namespace(|| "rcm_new"), + self.rcm_new.as_ref().map(|rcm_new| rcm_new.inner()), + )?; + + // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) + let cm_new = note_commit( + layouter.namespace(|| { + "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" + }), + config.sinsemilla_chip_2(), + config.ecc_chip(self.circuit_version.halo2_version()), + config.note_commit_chip_new(), + g_d_new.inner(), + pk_d_new.inner(), + v_new.clone(), + rho_new, + psi_new, + rcm_new, + None, + )?; + + let cmx = cm_new.extract_p(); + + // Constrain cmx to equal public input + layouter.constrain_instance(cmx.inner().cell(), config.primary, CMX)?; + } + + // Constrain the remaining Orchard circuit checks. + layouter.assign_region( + || "Orchard circuit checks", + |mut region| { + v_old.copy_advice(|| "v_old", &mut region, config.advices[0], 0)?; + v_new.copy_advice(|| "v_new", &mut region, config.advices[1], 0)?; + v_net_magnitude_sign.0.copy_advice( + || "v_net magnitude", + &mut region, + config.advices[2], + 0, + )?; + v_net_magnitude_sign.1.copy_advice( + || "v_net sign", + &mut region, + config.advices[3], + 0, + )?; + + root.copy_advice(|| "calculated root", &mut region, config.advices[4], 0)?; + region.assign_advice_from_instance( + || "pub input anchor", + config.primary, + ANCHOR, + config.advices[5], + 0, + )?; + + region.assign_advice_from_instance( + || "enable spend", + config.primary, + ENABLE_SPEND, + config.advices[6], + 0, + )?; + + region.assign_advice_from_instance( + || "enable output", + config.primary, + ENABLE_OUTPUT, + config.advices[7], + 0, + )?; + + config.q_orchard.enable(&mut region, 0) + }, + )?; + + Ok(AddressPoints { + g_d_old, + pk_d_old, + g_d_new, + pk_d_new, + }) + } + + /// Enforces the post-NU 6.3 cross-address restriction for one action: when + /// `disableCrossAddress` is nonzero, the spent note and output note must be + /// addressed to the same expanded receiver, meaning equal `(g_d, pk_d)`. + /// + /// This reuses the existing "Orchard circuit checks" gate instead of adding a + /// new gate. The gate already has a product constraint, + /// `v_old * (root - anchor) = 0`, with exactly the shape needed for + /// `disableCrossAddress * (old_coord - new_coord) = 0`. + /// + /// The post-NU 6.3 circuit enables that gate on four extra rows, one per affine coordinate of + /// `(g_d, pk_d)`, with each row wired as: + /// + /// ```text + /// v_old <- disableCrossAddress + /// v_new <- 0 (constant) + /// magnitude <- disableCrossAddress + /// sign <- 1 (constant) + /// root <- old coordinate + /// anchor <- new coordinate + /// enable_spend <- 1 (constant) + /// enable_output <- 1 (constant) + /// ``` + /// + /// With this layout, the gate constraints become: + /// + /// ```text + /// v_old - v_new = magnitude * sign -> disableCrossAddress - 0 = disableCrossAddress * 1 + /// v_old * (root - anchor) = 0 -> disableCrossAddress * (old_coord - new_coord) = 0 + /// v_old * (1 - enable_spend) = 0 -> disableCrossAddress * (1 - 1) = 0 + /// v_new * (1 - enable_output) = 0 -> 0 * (1 - 1) = 0 + /// ``` + /// + /// The second line is the actual cross-address check. Any nonzero + /// `disableCrossAddress` value forces each old coordinate to equal the + /// corresponding new coordinate. The public API encodes `disableCrossAddress` + /// as 0 or 1, but this algebra does not rely on a boolean constraint. + /// + /// The two otherwise-unused advice columns are also filled with copies of + /// `disableCrossAddress` so these rows occupy every advice column; that prevents + /// the floor planner from overlapping another selector-enabled region with the + /// check rows. + fn synthesize_cross_address_checks( + config: &Config, + layouter: &mut impl Layouter, + addrs: &AddressPoints, + ) -> Result<(), plonk::Error> { + let AddressPoints { + g_d_old, + pk_d_old, + g_d_new, + pk_d_new, + } = addrs; + + layouter.assign_region( + || "post-NU 6.3 cross-address checks", + |mut region| { + let coordinate_checks = [ + ("g_d x", g_d_old.inner().x(), g_d_new.inner().x()), + ("g_d y", g_d_old.inner().y(), g_d_new.inner().y()), + ("pk_d x", pk_d_old.inner().x(), pk_d_new.inner().x()), + ("pk_d y", pk_d_old.inner().y(), pk_d_new.inner().y()), + ]; + + for (offset, (label, old_coord, new_coord)) in + coordinate_checks.into_iter().enumerate() + { + // Copy disableCrossAddress from the public input at + // primary[DISABLE_CROSS_ADDRESS] into advices[0] for this + // coordinate-check row. + let cross_address_disabled = region.assign_advice_from_instance( + || "disableCrossAddress", + config.primary, + DISABLE_CROSS_ADDRESS, + config.advices[0], + offset, + )?; + + // Fill the v_new, magnitude, and sign cells so the reused + // value-balance constraint reads: + // disableCrossAddress - 0 = disableCrossAddress * 1. + region.assign_advice_from_constant( + || "zero", + config.advices[1], + offset, + pallas::Base::zero(), + )?; + cross_address_disabled.copy_advice( + || "disableCrossAddress magnitude", + &mut region, + config.advices[2], + offset, + )?; + region.assign_advice_from_constant( + || "positive sign", + config.advices[3], + offset, + pallas::Base::one(), + )?; + + // Copy the old coordinate into the gate's root cell and the + // new coordinate into its anchor cell for the equality check. + old_coord.copy_advice( + || format!("old {label}"), + &mut region, + config.advices[4], + offset, + )?; + new_coord.copy_advice( + || format!("new {label}"), + &mut region, + config.advices[5], + offset, + )?; + + // Set both enable flags to one so the unrelated enable checks + // in q_orchard are neutralized on these rows. + region.assign_advice_from_constant( + || "one (neutralize enable_spend check)", + config.advices[6], + offset, + pallas::Base::one(), + )?; + region.assign_advice_from_constant( + || "one (neutralize enable_output check)", + config.advices[7], + offset, + pallas::Base::one(), + )?; + + // Occupy the otherwise-unused rightmost advice columns so the + // floor planner cannot lay out another region (and enable its + // gate) on these rows. + cross_address_disabled.copy_advice( + || "disableCrossAddress padding", + &mut region, + config.advices[8], + offset, + )?; + cross_address_disabled.copy_advice( + || "disableCrossAddress padding", + &mut region, + config.advices[9], + offset, + )?; + + config.q_orchard.enable(&mut region, offset)?; + } + + Ok(()) + }, + ) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + use core::iter; + + use ff::Field; + use halo2_proofs::{circuit::Value, dev::MockProver}; + use pasta_curves::{pallas, vesta}; + use rand::{rngs::OsRng, RngCore}; + + use crate::{ + bundle::{BundleVersion, Flags}, + circuit::{ + Circuit, CircuitVanilla, Instance, OrchardCircuitVersion, Proof, ProvingKey, + SingleVerifier, VerifyingKey, K, + }, + keys::SpendValidatingKey, + note::{Note, NoteVersion, Rho}, + tree::MerklePath, + value::{ValueCommitTrapdoor, ValueCommitment}, + }; + + /// Generates a circuit and instance whose output note is addressed to an expanded + /// receiver distinct from the spent note's. + fn generate_circuit_instance( + rng: R, + circuit_version: OrchardCircuitVersion, + ) -> (Circuit, Instance) { + generate_circuit_instance_inner(rng, circuit_version, false) + } + + /// Generates a circuit and instance whose output note is addressed to the spent + /// note's expanded receiver, as the cross-address restriction requires. + fn generate_self_transfer_circuit_instance( + rng: R, + circuit_version: OrchardCircuitVersion, + ) -> (Circuit, Instance) { + generate_circuit_instance_inner(rng, circuit_version, true) + } + + fn generate_circuit_instance_inner( + mut rng: R, + circuit_version: OrchardCircuitVersion, + output_matches_spend: bool, + ) -> (Circuit, Instance) { + // Note Version does not matter for this + let note_version = NoteVersion::V2; + let (_, fvk, spent_note) = Note::dummy(&mut rng, None, note_version); + + let sender_address = spent_note.recipient(); + let nk = *fvk.nk(); + let rivk = fvk.rivk(fvk.scope_for_address(&spent_note.recipient()).unwrap()); + let nf_old = spent_note.nullifier(&fvk); + let rho = Rho::from_nf_old(nf_old); + let ak: SpendValidatingKey = fvk.into(); + let alpha = pallas::Scalar::random(&mut rng); + let rk = ak.randomize(&alpha); + + let output_note = if output_matches_spend { + Note::new( + sender_address, + spent_note.value(), + rho, + note_version, + &mut rng, + ) + } else { + loop { + let (_, _, output_note) = Note::dummy(&mut rng, Some(rho), note_version); + if !sender_address.same_expanded_receiver(&output_note.recipient()) { + break output_note; + } + } + }; + let cmx = output_note.commitment().into(); + + let value = spent_note.value() - output_note.value(); + let rcv = ValueCommitTrapdoor::random(&mut rng); + let cv_net = ValueCommitment::derive(value, rcv.clone()); + + let path = MerklePath::dummy(&mut rng); + let anchor = path.root(spent_note.commitment().into()); + + ( + Circuit { + common_witnesses: CircuitVanilla { + circuit_version, + path: Value::known(path.auth_path()), + pos: Value::known(path.position()), + g_d_old: Value::known(sender_address.g_d()), + pk_d_old: Value::known(*sender_address.pk_d()), + v_old: Value::known(spent_note.value()), + rho_old: Value::known(spent_note.rho()), + psi_old: Value::known(spent_note.psi()), + rcm_old: Value::known(spent_note.rcm()), + cm_old: Value::known(spent_note.commitment()), + alpha: Value::known(alpha), + ak: Value::known(ak), + nk: Value::known(nk), + rivk: Value::known(rivk), + g_d_new: Value::known(output_note.recipient().g_d()), + pk_d_new: Value::known(*output_note.recipient().pk_d()), + v_new: Value::known(output_note.value()), + psi_new: Value::known(output_note.psi()), + rcm_new: Value::known(output_note.rcm()), + rcv: Value::known(rcv), + }, + additional_zsa_witnesses: Value::unknown(), + }, + Instance { + anchor, + cv_net, + nf_old, + rk, + cmx, + enable_spend: true, + enable_output: true, + cross_address_disabled: false, + enable_zsa: false, + }, + ) + } + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + enum ProofFixtureEncoding { + LegacyTwoFlags, + PostNu6_3ThreeFlags, + } + + fn write_test_case( + mut w: W, + instance: &Instance, + proof: &Proof, + encoding: ProofFixtureEncoding, + ) -> std::io::Result<()> { + w.write_all(&instance.anchor().to_bytes())?; + w.write_all(&instance.cv_net().to_bytes())?; + w.write_all(&instance.nf_old().to_bytes())?; + w.write_all(&<[u8; 32]>::from(instance.rk()))?; + w.write_all(&instance.cmx().to_bytes())?; + match encoding { + ProofFixtureEncoding::LegacyTwoFlags => { + w.write_all(&[ + u8::from(instance.enable_spend()), + u8::from(instance.enable_output()), + ])?; + } + ProofFixtureEncoding::PostNu6_3ThreeFlags => { + w.write_all(&[ + u8::from(instance.enable_spend()), + u8::from(instance.enable_output()), + u8::from(instance.cross_address_disabled()), + ])?; + } + } + w.write_all(proof.as_ref())?; + Ok(()) + } + + fn read_test_case( + mut r: R, + encoding: ProofFixtureEncoding, + ) -> std::io::Result<(Instance, Proof)> { + let read_32_bytes = |r: &mut R| { + let mut ret = [0u8; 32]; + r.read_exact(&mut ret).unwrap(); + ret + }; + let read_bool = |r: &mut R| { + let mut byte = [0u8; 1]; + r.read_exact(&mut byte).unwrap(); + match byte { + [0] => false, + [1] => true, + _ => panic!("Unexpected non-boolean byte"), + } + }; + + let anchor = crate::Anchor::from_bytes(read_32_bytes(&mut r)).unwrap(); + let cv_net = ValueCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap(); + let nf_old = crate::note::Nullifier::from_bytes(&read_32_bytes(&mut r)).unwrap(); + let rk = read_32_bytes(&mut r).try_into().unwrap(); + let cmx = crate::note::ExtractedNoteCommitment::from_bytes(&read_32_bytes(&mut r)).unwrap(); + let enable_spend = read_bool(&mut r); + let enable_output = read_bool(&mut r); + let (cross_address_bit, bundle_version) = match encoding { + ProofFixtureEncoding::LegacyTwoFlags => (0, BundleVersion::orchard_v2()), + ProofFixtureEncoding::PostNu6_3ThreeFlags => { + // The fixture stores the instance-level *disable* bit; the NU6.3 flag + // byte carries the *enable* bit, so invert when reconstructing. + // + // The circuit is pool-agnostic; decode under Ironwood, the pool whose flag + // byte can represent `enableCrossAddress` either way (Orchard post-NU6.3 + // rejects bit 2). + let cross_address_disabled = read_bool(&mut r); + ( + u8::from(!cross_address_disabled) << 2, + BundleVersion::ironwood_v3(), + ) + } + }; + let flags = Flags::from_byte( + u8::from(enable_spend) | (u8::from(enable_output) << 1) | cross_address_bit, + bundle_version, + ) + .expect("test vectors use canonical flag encodings"); + + let instance = Instance::from_parts(anchor, cv_net, nf_old, rk, cmx, flags) + .expect("test vectors were generated with non-identity rk"); + + let mut proof_bytes = vec![]; + r.read_to_end(&mut proof_bytes)?; + let proof = Proof::new(proof_bytes); + + Ok((instance, proof)) + } + + #[test] + fn halo2_instance_includes_cross_address_disabled_flag() { + let (_, mut instance) = + generate_circuit_instance(OsRng, OrchardCircuitVersion::FixedPostNu6_2); + + let halo2_instance = instance.to_halo2_instance(); + assert_eq!(halo2_instance[0].len(), 11); + assert_eq!( + halo2_instance[0][super::DISABLE_CROSS_ADDRESS], + vesta::Scalar::zero() + ); + + instance.cross_address_disabled = true; + assert_eq!( + instance.to_halo2_instance()[0][super::DISABLE_CROSS_ADDRESS], + vesta::Scalar::one() + ); + } + + #[test] + fn cross_address_support_matches_circuit_version() { + assert!(!OrchardCircuitVersion::InsecurePreNu6_2.supports_cross_address_restriction()); + assert!(!OrchardCircuitVersion::FixedPostNu6_2.supports_cross_address_restriction()); + assert!(OrchardCircuitVersion::PostNu6_3.supports_cross_address_restriction()); + assert!(OrchardCircuitVersion::ZSA.supports_cross_address_restriction()); + } + + #[test] + fn post_nu6_3_cross_address_restriction_is_conditional() { + let mock_verify = |circuit: &Circuit, instance: &Instance| { + MockProver::run( + K, + &circuit.common_witnesses, + instance + .to_halo2_instance() + .iter() + .map(|p| p.to_vec()) + .collect(), + ) + .unwrap() + .verify() + }; + + // An unrestricted cross-address statement is satisfiable... + let (circuit, mut instance) = + generate_circuit_instance(OsRng, OrchardCircuitVersion::PostNu6_3); + assert_eq!(mock_verify(&circuit, &instance), Ok(())); + + // ...but setting `disableCrossAddress` makes it unsatisfiable... + instance.cross_address_disabled = true; + assert!(mock_verify(&circuit, &instance).is_err()); + + // ...while a restricted self-transfer statement is satisfiable. + let (circuit, mut instance) = + generate_self_transfer_circuit_instance(OsRng, OrchardCircuitVersion::PostNu6_3); + instance.cross_address_disabled = true; + assert_eq!(mock_verify(&circuit, &instance), Ok(())); + } + + #[test] + fn post_nu6_3_restricted_statement_proves_and_verifies() { + let mut rng = OsRng; + let (circuit, mut instance) = + generate_self_transfer_circuit_instance(&mut rng, OrchardCircuitVersion::PostNu6_3); + instance.cross_address_disabled = true; + + let pk = ProvingKey::build(OrchardCircuitVersion::PostNu6_3); + let vk = VerifyingKey::build(OrchardCircuitVersion::PostNu6_3); + + let proof = Proof::create( + &pk, + core::slice::from_ref(&circuit), + core::slice::from_ref(&instance), + &mut rng, + ) + .unwrap(); + assert!(proof.verify(&vk, core::slice::from_ref(&instance)).is_ok()); + } + + // FixedPostNu6_2 leaves instance row 9 (`disableCrossAddress`) unconstrained, so a + // freshly created proof can satisfy a restricted statement at the raw halo2 level + // without enforcing anything about addresses. This test documents that hazard and + // pins the API checks that close it. + #[test] + fn restricted_statement_requires_supporting_key() { + use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite}; + + let mut rng = OsRng; + let (circuit, mut instance) = + generate_circuit_instance(&mut rng, OrchardCircuitVersion::FixedPostNu6_2); + instance.cross_address_disabled = true; + + let pk = ProvingKey::build(OrchardCircuitVersion::FixedPostNu6_2); + let vk = VerifyingKey::build(OrchardCircuitVersion::FixedPostNu6_2); + + let raw_instances = instance.to_halo2_instance(); + let raw_instances: Vec<_> = raw_instances.iter().map(|i| &i[..]).collect(); + let raw_instances = [&raw_instances[..]]; + + let mut transcript = Blake2bWrite::<_, vesta::Affine, _>::init(vec![]); + super::plonk::create_proof( + &pk.params, + &pk.pk, + core::slice::from_ref(&circuit.common_witnesses), + &raw_instances, + &mut rng, + &mut transcript, + ) + .unwrap(); + let proof_bytes = transcript.finalize(); + + let strategy = SingleVerifier::new(&vk.params); + let mut transcript = Blake2bRead::init(&proof_bytes[..]); + assert!(super::plonk::verify_proof( + &vk.params, + &vk.vk, + strategy, + &raw_instances, + &mut transcript, + ) + .is_ok()); + + assert!(matches!( + Proof::create( + &pk, + core::slice::from_ref(&circuit), + core::slice::from_ref(&instance), + &mut rng, + ), + Err(super::plonk::Error::InvalidInstances), + )); + + let proof = Proof::new(proof_bytes); + assert!(matches!( + proof.verify(&vk, core::slice::from_ref(&instance)), + Err(super::plonk::Error::InvalidInstances), + )); + } + + // Every Vanilla circuit version leaves instance row 10 (`enableZSA`) unconstrained, so + // only a ZSA key may prove or verify `enable_zsa = 1`. + #[test] + fn zsa_statement_requires_supporting_key() { + let mut rng = OsRng; + let (circuit, mut instance) = + generate_circuit_instance(&mut rng, OrchardCircuitVersion::PostNu6_3); + instance.enable_zsa = true; + + let pk = ProvingKey::build(OrchardCircuitVersion::PostNu6_3); + assert!(matches!( + Proof::create( + &pk, + core::slice::from_ref(&circuit), + core::slice::from_ref(&instance), + &mut rng, + ), + Err(super::plonk::Error::InvalidInstances), + )); + + // The guard runs before the proof bytes are read, so an empty proof suffices. + let vk = VerifyingKey::build(OrchardCircuitVersion::PostNu6_3); + assert!(matches!( + Proof::new(Vec::new()).verify(&vk, core::slice::from_ref(&instance)), + Err(super::plonk::Error::InvalidInstances), + )); + } + + // Set ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF to regenerate the pinned circuit description + // for this version. + fn pinned_circuit_description( + circuit_version: OrchardCircuitVersion, + path: &str, + expected: &str, + ) -> VerifyingKey { + let vk = VerifyingKey::build(circuit_version); + + if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() { + std::fs::write(path, format!("{:#?}\n", vk.vk.pinned())) + .expect("should be able to write new circuit description"); + } else { + assert_eq!( + format!("{:#?}\n", vk.vk.pinned()), + expected.replace("\r\n", "\n") + ); + } + + vk + } + + // TODO: recast as a proptest + fn round_trip_for_version(circuit_version: OrchardCircuitVersion, vk: &VerifyingKey) { + let mut rng = OsRng; + + let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) + .map(|()| generate_circuit_instance(&mut rng, circuit_version)) + .unzip(); + + // Test that the proof size is as expected. + let expected_proof_size = { + let circuit_cost = + halo2_proofs::dev::CircuitCost::::measure( + K, + &circuits[0].common_witnesses, + ); + // These sizes are identical for every circuit version: the post-NU 6.3 circuit reuses the + // existing Orchard checks gate on spare rows and adds no columns or + // commitments, leaving the proof shape unchanged. + assert_eq!(usize::from(circuit_cost.proof_size(1)), 4992); + assert_eq!(usize::from(circuit_cost.proof_size(2)), 7264); + // The constants in `Proof::expected_proof_size` must track the circuit's actual + // proof size; this guards them against drift if the circuit ever changes. + let circuit_version = BundleVersion::orchard_v3().circuit_version(); + assert_eq!(Proof::expected_proof_size(circuit_version, 1), 4992); + assert_eq!(Proof::expected_proof_size(circuit_version, 2), 7264); + assert_eq!( + Proof::expected_proof_size(circuit_version, instances.len()), + usize::from(circuit_cost.proof_size(instances.len())), + ); + usize::from(circuit_cost.proof_size(instances.len())) + }; + + for (circuit, instance) in circuits.iter().zip(instances.iter()) { + assert_eq!( + MockProver::run( + K, + &circuit.common_witnesses, + instance + .to_halo2_instance() + .iter() + .map(|p| p.to_vec()) + .collect() + ) + .unwrap() + .verify(), + Ok(()) + ); + } + + let pk = ProvingKey::build(circuit_version); + let proof = Proof::create(&pk, &circuits, &instances, &mut rng).unwrap(); + assert!(proof.verify(vk, &instances).is_ok()); + assert_eq!(proof.0.len(), expected_proof_size); + } + + #[test] + fn round_trip_fixed() { + let vk = pinned_circuit_description( + OrchardCircuitVersion::FixedPostNu6_2, + "src/circuit_data/circuit_description_fixed", + include_str!("../circuit_data/circuit_description_fixed"), + ); + round_trip_for_version(OrchardCircuitVersion::FixedPostNu6_2, &vk); + } + + #[test] + fn round_trip_post_nu6_3() { + let vk = pinned_circuit_description( + OrchardCircuitVersion::PostNu6_3, + "src/circuit_data/circuit_description_post_nu6_3", + include_str!("../circuit_data/circuit_description_post_nu6_3"), + ); + round_trip_for_version(OrchardCircuitVersion::PostNu6_3, &vk); + } + + // Proves with the proving key for `proving_version` and checks that the proof verifies under + // the verifying key for the same version, but not under a version with a different verifying + // key. + fn proof_is_rejected_by_other_circuit_version( + proving_version: OrchardCircuitVersion, + other_version: OrchardCircuitVersion, + ) { + let mut rng = OsRng; + + let (circuit, instance) = generate_circuit_instance(&mut rng, proving_version); + let instances = core::slice::from_ref(&instance); + + let pk = ProvingKey::build(proving_version); + let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); + + // Verifies under the matching version's verifying key. + let vk_matching = VerifyingKey::build(proving_version); + assert!(proof.verify(&vk_matching, instances).is_ok()); + + // Does not verify under the other version's verifying key. + let vk_other = VerifyingKey::build(other_version); + assert!(proof.verify(&vk_other, instances).is_err()); + } + + #[test] + fn proof_verifies_against_matching_circuit_version() { + // Insecure proofs are rejected by the anchored circuit versions, and anchored proofs are + // rejected by the insecure verifying key. + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::FixedPostNu6_2, + OrchardCircuitVersion::InsecurePreNu6_2, + ); + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::PostNu6_3, + OrchardCircuitVersion::InsecurePreNu6_2, + ); + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::InsecurePreNu6_2, + OrchardCircuitVersion::FixedPostNu6_2, + ); + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::InsecurePreNu6_2, + OrchardCircuitVersion::PostNu6_3, + ); + } + + #[test] + fn fixed_and_post_nu6_3_have_distinct_verifying_keys() { + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::FixedPostNu6_2, + OrchardCircuitVersion::PostNu6_3, + ); + proof_is_rejected_by_other_circuit_version( + OrchardCircuitVersion::PostNu6_3, + OrchardCircuitVersion::FixedPostNu6_2, + ); + } + + // Proving a circuit with a proving key for a different circuit version is a misuse: the + // proving key and circuits must agree (see `Proof::create`). Confirm `create` rejects it + // with `plonk::Error::Synthesis` rather than emitting an unverifiable proof. + #[test] + fn create_rejects_mismatched_proving_key_version() { + let mut rng = OsRng; + + for (circuit_version, pk_version) in [ + ( + OrchardCircuitVersion::InsecurePreNu6_2, + OrchardCircuitVersion::FixedPostNu6_2, + ), + ( + OrchardCircuitVersion::FixedPostNu6_2, + OrchardCircuitVersion::PostNu6_3, + ), + ( + OrchardCircuitVersion::PostNu6_3, + OrchardCircuitVersion::FixedPostNu6_2, + ), + ] { + let (circuit, instance) = generate_circuit_instance(&mut rng, circuit_version); + let instances = core::slice::from_ref(&instance); + + let mismatched_pk = ProvingKey::build(pk_version); + + assert!(matches!( + Proof::create(&mismatched_pk, &[circuit], instances, &mut rng), + Err(super::plonk::Error::Synthesis), + )); + } + } + + fn serialized_proof_test_case_for_version( + circuit_version: OrchardCircuitVersion, + proof_path: &str, + test_case_bytes: &[u8], + encoding: ProofFixtureEncoding, + expected_proof_size: usize, + restricted: bool, + ) { + let vk = VerifyingKey::build(circuit_version); + // Set ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF to regenerate this serialized proof + // fixture. The non-regeneration path embeds and verifies the checked-in fixture. + if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() { + let create_proof = || -> std::io::Result<()> { + let mut rng = OsRng; + + let (circuit, mut instance) = if restricted { + generate_self_transfer_circuit_instance(&mut rng, circuit_version) + } else { + generate_circuit_instance(&mut rng, circuit_version) + }; + instance.cross_address_disabled = restricted; + let instances = core::slice::from_ref(&instance); + + let pk = ProvingKey::build(circuit_version); + let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); + assert!(proof.verify(&vk, instances).is_ok()); + + let file = std::fs::File::create(proof_path)?; + write_test_case(file, &instance, &proof, encoding) + }; + create_proof().expect("should be able to write new proof"); + // Regeneration only writes the fixture; the non-generate run below embeds and + // verifies it. + return; + } + + // Parse the hardcoded proof test case. + let (instance, proof) = + read_test_case(test_case_bytes, encoding).expect("proof must be valid"); + assert_eq!(instance.cross_address_disabled(), restricted); + assert_eq!(proof.0.len(), expected_proof_size); + + assert!(proof.verify(&vk, &[instance]).is_ok()); + } + + #[test] + fn serialized_fixed_proof_test_case() { + serialized_proof_test_case_for_version( + OrchardCircuitVersion::FixedPostNu6_2, + "src/circuit_data/circuit_proof_test_case_fixed.bin", + include_bytes!("../circuit_data/circuit_proof_test_case_fixed.bin"), + ProofFixtureEncoding::LegacyTwoFlags, + 4992, + false, + ); + } + + #[test] + fn serialized_post_nu6_3_proof_test_case() { + serialized_proof_test_case_for_version( + OrchardCircuitVersion::PostNu6_3, + "src/circuit_data/circuit_proof_test_case_post_nu6_3.bin", + include_bytes!("../circuit_data/circuit_proof_test_case_post_nu6_3.bin"), + ProofFixtureEncoding::PostNu6_3ThreeFlags, + 4992, + false, + ); + } + + #[test] + fn serialized_post_nu6_3_restricted_proof_test_case() { + serialized_proof_test_case_for_version( + OrchardCircuitVersion::PostNu6_3, + "src/circuit_data/circuit_proof_test_case_post_nu6_3_restricted.bin", + include_bytes!("../circuit_data/circuit_proof_test_case_post_nu6_3_restricted.bin"), + ProofFixtureEncoding::PostNu6_3ThreeFlags, + 4992, + true, + ); + } + + // The deployed (NU5..NU6.2) verifying key and a pre-fix proof. `InsecurePreNu6_2` + // reconstructs the historical circuit, so this checks that the deployed verifying key is + // reproduced exactly and that the old proof still verifies under it — the guarantee that + // lets a node sync from before the fix. These fixtures are frozen as the canonical + // pre-NU6.2 verifying key and a sample proof, so they are never regenerated. + #[test] + fn insecure_against_stored_circuit() { + let vk = VerifyingKey::build(OrchardCircuitVersion::InsecurePreNu6_2); + assert_eq!( + format!("{:#?}\n", vk.vk.pinned()), + include_str!("../circuit_data/circuit_description_insecure").replace("\r\n", "\n") + ); + + let (instance, proof) = { + let test_case_bytes = + include_bytes!("../circuit_data/circuit_proof_test_case_insecure.bin"); + read_test_case(&test_case_bytes[..], ProofFixtureEncoding::LegacyTwoFlags) + .expect("proof must be valid") + }; + assert_eq!(proof.0.len(), 4992); + assert!(proof.verify(&vk, &[instance]).is_ok()); + } + + #[cfg(feature = "dev-graph")] + #[test] + fn print_action_circuit() { + use plotters::prelude::*; + + let root = BitMapBackend::new("action-circuit-layout.png", (1024, 768)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root + .titled("Orchard Action Circuit", ("sans-serif", 60)) + .unwrap(); + + let circuit = CircuitVanilla::empty(OrchardCircuitVersion::FixedPostNu6_2); + halo2_proofs::dev::CircuitLayout::default() + .show_labels(false) + .view_height(0..(1 << 11)) + .render(K, &circuit, &root) + .unwrap(); + } +} diff --git a/src/circuit/circuit_zsa.rs b/src/circuit/circuit_zsa.rs new file mode 100644 index 000000000..460baf9ce --- /dev/null +++ b/src/circuit/circuit_zsa.rs @@ -0,0 +1,1488 @@ +//! The Orchard Action circuit implementation for the ZSA variation of the Orchard protocol. +//! +//! Includes the configuration, synthesis, and proof verification logic. + +use ff::Field; + +use group::Curve; + +use pasta_curves::{arithmetic::CurveAffine, pallas}; + +use halo2_gadgets::{ + ecc::{FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarVar}, + sinsemilla::{chip::SinsemillaChip, merkle::MerklePath}, + utilities::{bool_check, lookup_range_check::PallasLookupRangeCheck4_5BConfig}, +}; + +use halo2_proofs::{ + circuit::{floor_planner, Layouter, Value}, + plonk::{self, Advice, Column, Constraints, Expression, Selector}, + poly::Rotation, +}; + +use crate::{ + circuit::{ + commit_ivk::gadgets::commit_ivk, + configure_circuit, + derive_nullifier::{gadgets::derive_nullifier, ZsaNullifierParams}, + gadget::{assign_free_advice, assign_is_zatoshi_asset, assign_split_flag}, + note_commit::{gadgets::note_commit, ZsaNoteCommitParams}, + value_commit_orchard::{gadgets::value_commit_orchard, ZsaValueCommitParams}, + AddressPoints, CircuitVanilla, Config, OrchardCircuitVersion, ANCHOR, CMX, CV_NET_X, + CV_NET_Y, DISABLE_CROSS_ADDRESS, ENABLE_OUTPUT, ENABLE_SPEND, ENABLE_ZSA, NF_OLD, RK_X, + RK_Y, + }, + constants::{OrchardFixedBasesFull, OrchardHashDomains}, + note::AssetBase, +}; + +/// The ZSA-specific witnesses. +#[derive(Clone, Debug)] +pub(crate) struct AdditionalZsaWitnesses { + pub(crate) psi_nf: pallas::Base, + pub(crate) asset: AssetBase, + pub(crate) split_flag: bool, +} + +fn unpack( + zsa_values: Value, +) -> (Value, Value, Value) { + ( + zsa_values.clone().map(|values| values.psi_nf), + zsa_values.clone().map(|values| values.asset), + zsa_values.map(|values| values.split_flag), + ) +} + +/// The OrchardZSA Action circuit. +#[derive(Clone, Debug)] +pub struct CircuitZsa { + pub(crate) common_witnesses: CircuitVanilla, + + // The ZSA-specific witnesses. + pub(crate) additional_zsa_witnesses: Value, +} + +impl CircuitZsa { + /// Returns an empty circuit with all private witnesses unknown. + /// + /// This is used for circuit shape-dependent operations, such as generating keys + /// or rendering the circuit layout, where witness values are not required. + pub(crate) fn empty() -> Self { + CircuitZsa { + common_witnesses: CircuitVanilla::empty(OrchardCircuitVersion::ZSA), + additional_zsa_witnesses: Value::unknown(), + } + } +} + +/// Creates the `q_orchard` gate checking the OrchardZSA Action statement, on the given +/// selector. +pub(super) fn configure_zsa_orchard_gate( + meta: &mut plonk::ConstraintSystem, + advices: [Column; 10], + q_orchard: Selector, +) { + // The new or updated constraints for OrchardZSA are explained in + // [ZIP-226: Transfer and Burn of Zcash Shielded Assets][circuitstatement]. + // + // All OrchardZSA constraints: + // Constrain split_flag to be boolean + // Constrain v_old * (1 - split_flag) - v_new = magnitude * sign + // Constrain (v_old = 0 and is_zatoshi_asset = 1) or (calculated root = anchor) + // Constrain v_old = 0 or enable_spend = 1 + // Constrain v_new = 0 or enable_output = 1 + // Constrain is_zatoshi_asset to be boolean + // Constrain if is_zatoshi_asset = 1 then asset = zatoshi_asset else asset != zatoshi_asset + // Constrain if split_flag = 0 then psi_old = psi_nf + // Constrain if split_flag = 1, then is_zatoshi_asset = 0 + // Constrain if enable_zsa = 0, then is_zatoshi_asset = 1 + // Constrain if disable_cross_address = 1, then split_flag = 0 + // + // [circuitstatement]: https://zips.z.cash/zip-0226#circuit-statement + meta.create_gate("Orchard circuit checks", |meta| { + let q_orchard = meta.query_selector(q_orchard); + let v_old = meta.query_advice(advices[0], Rotation::cur()); + let v_new = meta.query_advice(advices[1], Rotation::cur()); + let magnitude = meta.query_advice(advices[2], Rotation::cur()); + let sign = meta.query_advice(advices[3], Rotation::cur()); + + let root = meta.query_advice(advices[4], Rotation::cur()); + let anchor = meta.query_advice(advices[5], Rotation::cur()); + + let enable_spend = meta.query_advice(advices[6], Rotation::cur()); + let enable_output = meta.query_advice(advices[7], Rotation::cur()); + + let split_flag = meta.query_advice(advices[8], Rotation::cur()); + + let is_zatoshi_asset = meta.query_advice(advices[9], Rotation::cur()); + let asset_x = meta.query_advice(advices[0], Rotation::next()); + let asset_y = meta.query_advice(advices[1], Rotation::next()); + let diff_asset_x_inv = meta.query_advice(advices[2], Rotation::next()); + let diff_asset_y_inv = meta.query_advice(advices[3], Rotation::next()); + + let one = Expression::Constant(pallas::Base::one()); + + let zatoshi_asset = AssetBase::zatoshi() + .cv_base() + .to_affine() + .coordinates() + .unwrap(); + + let diff_asset_x = asset_x - Expression::Constant(*zatoshi_asset.x()); + let diff_asset_y = asset_y - Expression::Constant(*zatoshi_asset.y()); + + let psi_old = meta.query_advice(advices[4], Rotation::next()); + let psi_nf = meta.query_advice(advices[5], Rotation::next()); + + let enable_zsa = meta.query_advice(advices[6], Rotation::next()); + let disable_cross_address = meta.query_advice(advices[7], Rotation::next()); + + Constraints::with_selector( + q_orchard, + [ + ("bool_check split_flag", bool_check(split_flag.clone())), + ( + "v_old * (1 - split_flag) - v_new = magnitude * sign", + v_old.clone() * (one.clone() - split_flag.clone()) + - v_new.clone() + - magnitude * sign, + ), + // We already checked that + // * is_zatoshi_asset is boolean (just below), and + // * v_old is a 64 bit unsigned integer (in the note commitment evaluation). + // So, 1 - is_zatoshi_asset + v_old = 0 only when (is_zatoshi_asset = 1 and v_old = 0), no overflow can occur. + ( + "(v_old = 0 and is_zatoshi_asset = 1) or (root = anchor)", + (v_old.clone() + one.clone() - is_zatoshi_asset.clone()) * (root - anchor), + ), + ( + "v_old = 0 or enable_spend = 1", + v_old * (one.clone() - enable_spend), + ), + ( + "v_new = 0 or enable_output = 1", + v_new * (one.clone() - enable_output), + ), + ( + "bool_check is_zatoshi_asset", + bool_check(is_zatoshi_asset.clone()), + ), + ( + "(is_zatoshi_asset = 1) => (asset_x = zatoshi_asset_x)", + is_zatoshi_asset.clone() * diff_asset_x.clone(), + ), + ( + "(is_zatoshi_asset = 1) => (asset_y = zatoshi_asset_y)", + is_zatoshi_asset.clone() * diff_asset_y.clone(), + ), + // To prove that `asset` is not equal to `zatoshi_asset`, we will prove that at + // least one of `x(asset) - x(zatoshi_asset)` or `y(asset) - y(zatoshi_asset)` is + // not equal to zero. + // To prove that `x(asset) - x(zatoshi_asset)` (resp `y(asset) - y(zatoshi_asset)`) + // is not equal to zero, we will prove that it is invertible. + ( + "(is_zatoshi_asset = 0) => (asset != zatoshi_asset)", + (one.clone() - is_zatoshi_asset.clone()) + * (diff_asset_x * diff_asset_x_inv - one.clone()) + * (diff_asset_y * diff_asset_y_inv - one.clone()), + ), + ( + "(split_flag = 0) => (psi_old = psi_nf)", + (one.clone() - split_flag.clone()) * (psi_old - psi_nf), + ), + ( + "(split_flag = 1) => (is_zatoshi_asset = 0)", + split_flag.clone() * is_zatoshi_asset.clone(), + ), + ( + "(enable_zsa = 0) => (is_zatoshi_asset = 1)", + (one.clone() - enable_zsa) * (one - is_zatoshi_asset), + ), + // `split_flag` and `cross_address_disabled` cannot both be enabled at the + // same time. A split note's output is forced to a negative net value, which + // is not a meaningful self-transfer. + ( + "(disable_cross_address = 1) => (split_flag = 0)", + split_flag * disable_cross_address, + ), + ], + ) + }); +} + +impl plonk::Circuit for CircuitZsa { + type Config = Config; + type FloorPlanner = floor_planner::V1; + + fn without_witnesses(&self) -> Self { + CircuitZsa::empty() + } + + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { + configure_circuit(meta, true) + } + + #[allow(non_snake_case)] + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), plonk::Error> { + if !self.common_witnesses.circuit_version.is_zsa() { + return Err(plonk::Error::Synthesis); + } + + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?; + + // Unpack the ZSA witnesses. + let (psi_nf_value, asset_value, split_flag_value) = + unpack(self.additional_zsa_witnesses.clone()); + + // Construct the ECC chip. + let ecc_chip = config.ecc_chip(self.common_witnesses.circuit_version.halo2_version()); + + // Witness private inputs that are used across multiple checks. + let (psi_nf, psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new, asset) = { + // Witness psi_nf + let psi_nf = assign_free_advice( + layouter.namespace(|| "witness psi_nf"), + config.advices[0], + psi_nf_value, + )?; + + // Witness psi_old + let psi_old = assign_free_advice( + layouter.namespace(|| "witness psi_old"), + config.advices[0], + self.common_witnesses.psi_old, + )?; + + // Witness rho_old + let rho_old = assign_free_advice( + layouter.namespace(|| "witness rho_old"), + config.advices[0], + self.common_witnesses.rho_old.map(|rho| rho.into_inner()), + )?; + + // Witness cm_old + let cm_old = Point::new( + ecc_chip.clone(), + layouter.namespace(|| "cm_old"), + self.common_witnesses + .cm_old + .as_ref() + .map(|cm| cm.inner().to_affine()), + )?; + + // Witness g_d_old + let g_d_old = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "gd_old"), + self.common_witnesses + .g_d_old + .as_ref() + .map(|gd| gd.to_affine()), + )?; + + // Witness ak_P. + let ak_P: Value = self.common_witnesses.ak.as_ref().map(|ak| ak.into()); + let ak_P = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness ak_P"), + ak_P.map(|ak_P| ak_P.to_affine()), + )?; + + // Witness nk. + let nk = assign_free_advice( + layouter.namespace(|| "witness nk"), + config.advices[0], + self.common_witnesses.nk.map(|nk| nk.inner()), + )?; + + // Witness v_old. + let v_old = assign_free_advice( + layouter.namespace(|| "witness v_old"), + config.advices[0], + self.common_witnesses.v_old, + )?; + + // Witness v_new. + let v_new = assign_free_advice( + layouter.namespace(|| "witness v_new"), + config.advices[0], + self.common_witnesses.v_new, + )?; + + // Witness asset + let asset = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness asset"), + asset_value.map(|asset| asset.cv_base().to_affine()), + )?; + + ( + psi_nf, psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new, asset, + ) + }; + + // Witness split_flag + let split_flag = assign_split_flag( + layouter.namespace(|| "witness split_flag"), + config.advices[0], + split_flag_value, + )?; + + // Witness is_zatoshi_asset which is equal to + // 1 if asset is equal to zatoshi asset, and + // 0 if asset is not equal to zatoshi asset. + let is_zatoshi_asset = assign_is_zatoshi_asset( + layouter.namespace(|| "witness is_zatoshi_asset"), + config.advices[0], + asset_value, + )?; + + // Merkle path validity check. + let root = { + let path = self + .common_witnesses + .path + .map(|typed_path| typed_path.map(|node| node.inner())); + let merkle_inputs = MerklePath::construct( + [config.merkle_chip_1(), config.merkle_chip_2()], + OrchardHashDomains::MerkleCrh, + self.common_witnesses.pos, + path, + ); + let leaf = cm_old.extract_p().inner().clone(); + merkle_inputs.calculate_root(layouter.namespace(|| "Merkle path"), leaf)? + }; + + // Value commitment integrity. + // See [ZIP-226: Transfer and Burn of Zcash Shielded Assets][valuecommitcorrectness] for more details. + // + // [valuecommitcorrectness]: https://zips.z.cash/zip-0226#value-commitment-correctness + let v_net_magnitude_sign = { + // Witness the magnitude and sign of v_net = v_old - v_new + let v_net_magnitude_sign = { + // v_net is equal to + // (-v_new) if split_flag = true + // v_old - v_new if split_flag = false + let v_net = split_flag_value.and_then(|split_flag| { + if split_flag { + Value::known(crate::value::NoteValue::ZERO) - self.common_witnesses.v_new + } else { + self.common_witnesses.v_old - self.common_witnesses.v_new + } + }); + + let magnitude_sign = v_net.map(|v_net| { + let (magnitude, sign) = v_net.magnitude_sign(); + + ( + // magnitude is guaranteed to be an unsigned 64-bit value. + // Therefore, we can move it into the base field. + pallas::Base::from(magnitude), + match sign { + crate::value::Sign::Positive => pallas::Base::one(), + crate::value::Sign::Negative => -pallas::Base::one(), + }, + ) + }); + + let magnitude = assign_free_advice( + layouter.namespace(|| "v_net magnitude"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.0), + )?; + let sign = assign_free_advice( + layouter.namespace(|| "v_net sign"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.1), + )?; + (magnitude, sign) + }; + + let rcv = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcv"), + self.common_witnesses.rcv.as_ref().map(|rcv| rcv.inner()), + )?; + + let cv_net = value_commit_orchard( + layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net_magnitude_sign)"), + ecc_chip.clone(), + v_net_magnitude_sign.clone(), + rcv, + Some(ZsaValueCommitParams { + sinsemilla_chip: config.sinsemilla_chip_1(), + asset_base: asset.clone(), + }), + )?; + + // Constrain cv_net to equal public input + layouter.constrain_instance(cv_net.inner().x().cell(), config.primary, CV_NET_X)?; + layouter.constrain_instance(cv_net.inner().y().cell(), config.primary, CV_NET_Y)?; + + // Return the magnitude and sign so we can use them in the Orchard gate. + v_net_magnitude_sign + }; + + // Nullifier integrity. + // See [ZIP-226: Transfer and Burn of Zcash Shielded Assets][zip226] for more details. + // + // [zip226]: https://zips.z.cash/zip-0226 + let nf_old = { + let nf_old = derive_nullifier( + layouter.namespace(|| "nf_old = DeriveNullifier_nk(rho_old, psi_nf, cm_old)"), + config.poseidon_chip(), + config.add_chip(), + ecc_chip.clone(), + rho_old.clone(), + &psi_nf, + &cm_old, + nk.clone(), + Some(ZsaNullifierParams { + cond_swap_chip: config.cond_swap_chip(), + split_flag: split_flag.clone(), + }), + )?; + + // Constrain nf_old to equal public input + layouter.constrain_instance(nf_old.inner().cell(), config.primary, NF_OLD)?; + + nf_old + }; + + // Spend authority + { + let alpha = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "alpha"), + self.common_witnesses.alpha, + )?; + + // alpha_commitment = [alpha] SpendAuthG + let (alpha_commitment, _) = { + let spend_auth_g = OrchardFixedBasesFull::SpendAuthG; + let spend_auth_g = FixedPoint::from_inner(ecc_chip.clone(), spend_auth_g); + spend_auth_g.mul(layouter.namespace(|| "[alpha] SpendAuthG"), alpha)? + }; + + // [alpha] SpendAuthG + ak_P + let rk = alpha_commitment.add(layouter.namespace(|| "rk"), &ak_P)?; + + // Constrain rk to equal public input + layouter.constrain_instance(rk.inner().x().cell(), config.primary, RK_X)?; + layouter.constrain_instance(rk.inner().y().cell(), config.primary, RK_Y)?; + } + + // Diversified address integrity. + let pk_d_old = { + let ivk = { + let ak = ak_P.extract_p().inner().clone(); + let rivk = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rivk"), + self.common_witnesses.rivk.map(|rivk| rivk.inner()), + )?; + + commit_ivk( + config.sinsemilla_chip_1(), + ecc_chip.clone(), + config.commit_ivk_chip(), + layouter.namespace(|| "CommitIvk"), + ak, + nk, + rivk, + )? + }; + let ivk = + ScalarVar::from_base(ecc_chip.clone(), layouter.namespace(|| "ivk"), ivk.inner())?; + + // [ivk] g_d_old + // The scalar value is passed through and discarded. + let (derived_pk_d_old, _ivk) = + g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk)?; + + // Constrain derived pk_d_old to equal witnessed pk_d_old + // + // This equality constraint is technically superfluous, because the assigned + // value of `derived_pk_d_old` is an equivalent witness. But it's nice to see + // an explicit connection between circuit-synthesized values, and explicit + // prover witnesses. We could get the best of both worlds with a write-on-copy + // abstraction (https://github.com/zcash/halo2/issues/334). + let pk_d_old = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d_old"), + self.common_witnesses + .pk_d_old + .map(|pk_d_old| pk_d_old.inner().to_affine()), + )?; + derived_pk_d_old + .constrain_equal(layouter.namespace(|| "pk_d_old equality"), &pk_d_old)?; + + pk_d_old + }; + + // Old note commitment integrity. + // See [ZIP-226: Transfer and Burn of Zcash Shielded Assets][notecommit] for more details. + // + // [notecommit]: https://zips.z.cash/zip-0226#note-structure-commitment. + { + let rcm_old = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcm_old"), + self.common_witnesses + .rcm_old + .as_ref() + .map(|rcm_old| rcm_old.inner()), + )?; + + // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) + let derived_cm_old = note_commit( + layouter.namespace(|| { + "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" + }), + config.sinsemilla_chip_1(), + config.ecc_chip(self.common_witnesses.circuit_version.halo2_version()), + config.note_commit_chip_old(), + g_d_old.inner(), + pk_d_old.inner(), + v_old.clone(), + rho_old, + psi_old.clone(), + rcm_old, + Some(ZsaNoteCommitParams { + cond_swap_chip: config.cond_swap_chip(), + asset: asset.inner().clone(), + is_zatoshi_asset: is_zatoshi_asset.clone(), + }), + )?; + + // Constrain derived cm_old to equal witnessed cm_old + derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; + } + + // Witness g_d_new + let g_d_new = { + let g_d_new = self + .common_witnesses + .g_d_new + .map(|g_d_new| g_d_new.to_affine()); + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness g_d_new_star"), + g_d_new, + )? + }; + + // Witness pk_d_new + let pk_d_new = { + let pk_d_new = self + .common_witnesses + .pk_d_new + .map(|pk_d_new| pk_d_new.inner().to_affine()); + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d_new"), + pk_d_new, + )? + }; + + // New note commitment integrity. + // See [ZIP-226: Transfer and Burn of Zcash Shielded Assets][notecommit] for more details. + // + // [notecommit]: https://zips.z.cash/zip-0226#note-structure-commitment. + { + // ρ^new = nf^old + let rho_new = nf_old.inner().clone(); + + // Witness psi_new + let psi_new = assign_free_advice( + layouter.namespace(|| "witness psi_new"), + config.advices[0], + self.common_witnesses.psi_new, + )?; + + let rcm_new = ScalarFixed::new( + ecc_chip, + layouter.namespace(|| "rcm_new"), + self.common_witnesses + .rcm_new + .as_ref() + .map(|rcm_new| rcm_new.inner()), + )?; + + // g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi) + let cm_new = note_commit( + layouter.namespace(|| { + "g★_d || pk★_d || i2lebsp_{64}(v) || i2lebsp_{255}(rho) || i2lebsp_{255}(psi)" + }), + config.sinsemilla_chip_2(), + config.ecc_chip(self.common_witnesses.circuit_version.halo2_version()), + config.note_commit_chip_new(), + g_d_new.inner(), + pk_d_new.inner(), + v_new.clone(), + rho_new, + psi_new, + rcm_new, + Some(ZsaNoteCommitParams { + cond_swap_chip: config.cond_swap_chip(), + asset: asset.inner().clone(), + is_zatoshi_asset: is_zatoshi_asset.clone(), + }), + )?; + + let cmx = cm_new.extract_p(); + + // Constrain cmx to equal public input + layouter.constrain_instance(cmx.inner().cell(), config.primary, CMX)?; + } + + // Constrain the remaining Orchard circuit checks. + layouter.assign_region( + || "Orchard circuit checks", + |mut region| { + v_old.copy_advice(|| "v_old", &mut region, config.advices[0], 0)?; + v_new.copy_advice(|| "v_new", &mut region, config.advices[1], 0)?; + v_net_magnitude_sign.0.copy_advice( + || "v_net magnitude", + &mut region, + config.advices[2], + 0, + )?; + v_net_magnitude_sign.1.copy_advice( + || "v_net sign", + &mut region, + config.advices[3], + 0, + )?; + + root.copy_advice(|| "calculated root", &mut region, config.advices[4], 0)?; + region.assign_advice_from_instance( + || "pub input anchor", + config.primary, + ANCHOR, + config.advices[5], + 0, + )?; + + region.assign_advice_from_instance( + || "enable spend", + config.primary, + ENABLE_SPEND, + config.advices[6], + 0, + )?; + + region.assign_advice_from_instance( + || "enable output", + config.primary, + ENABLE_OUTPUT, + config.advices[7], + 0, + )?; + + split_flag.copy_advice(|| "split_flag", &mut region, config.advices[8], 0)?; + + is_zatoshi_asset.copy_advice( + || "is_zatoshi_asset", + &mut region, + config.advices[9], + 0, + )?; + asset + .inner() + .x() + .copy_advice(|| "asset_x", &mut region, config.advices[0], 1)?; + asset + .inner() + .y() + .copy_advice(|| "asset_y", &mut region, config.advices[1], 1)?; + + // `diff_asset_x_inv` and `diff_asset_y_inv` will be used to prove that + // if is_zatoshi_asset = 0, then asset != zatoshi_asset. + region.assign_advice( + || "diff_asset_x_inv", + config.advices[2], + 1, + || { + asset_value.map(|asset| { + let asset_x = *asset.cv_base().to_affine().coordinates().unwrap().x(); + let zatoshi_asset_x = *AssetBase::zatoshi() + .cv_base() + .to_affine() + .coordinates() + .unwrap() + .x(); + + let diff_asset_x = asset_x - zatoshi_asset_x; + + if diff_asset_x == pallas::Base::zero() { + pallas::Base::zero() + } else { + diff_asset_x.invert().unwrap() + } + }) + }, + )?; + region.assign_advice( + || "diff_asset_y_inv", + config.advices[3], + 1, + || { + asset_value.map(|asset| { + let asset_y = *asset.cv_base().to_affine().coordinates().unwrap().y(); + let zatoshi_asset_y = *AssetBase::zatoshi() + .cv_base() + .to_affine() + .coordinates() + .unwrap() + .y(); + + let diff_asset_y = asset_y - zatoshi_asset_y; + + if diff_asset_y == pallas::Base::zero() { + pallas::Base::zero() + } else { + diff_asset_y.invert().unwrap() + } + }) + }, + )?; + + psi_old.copy_advice(|| "psi_old", &mut region, config.advices[4], 1)?; + psi_nf.copy_advice(|| "psi_nf", &mut region, config.advices[5], 1)?; + + region.assign_advice_from_instance( + || "enable zsa", + config.primary, + ENABLE_ZSA, + config.advices[6], + 1, + )?; + + region.assign_advice_from_instance( + || "disable_cross_address", + config.primary, + DISABLE_CROSS_ADDRESS, + config.advices[7], + 1, + )?; + + config.q_orchard.enable(&mut region, 0) + }, + )?; + + let addrs = AddressPoints { + g_d_old, + pk_d_old, + g_d_new, + pk_d_new, + }; + + synthesize_cross_address_checks(&config, &mut layouter, &addrs)?; + + Ok(()) + } +} + +/// Enforces the ZSA cross-address restriction for one action: when +/// `disableCrossAddress` is nonzero, the spent note and output note must be +/// addressed to the same expanded receiver, meaning equal `(g_d, pk_d)`. +/// +/// This reuses the existing "Orchard circuit checks" gate instead of adding a +/// new gate. The gate already has a product constraint, +/// `(v_old + 1 - is_zatoshi_asset) * (root - anchor) = 0`, with exactly the shape needed for +/// `disableCrossAddress * (old_coord - new_coord) = 0` when `is_zatoshi_asset = 1`. +/// +/// The ZSA circuit enables that gate on eight extra rows, two per affine coordinate of +/// `(g_d, pk_d)`, with: +/// +/// ```text +/// First row: +/// v_old <- disableCrossAddress +/// v_new <- 0 (constant) +/// magnitude <- disableCrossAddress +/// sign <- 1 (constant) +/// root <- old coordinate +/// anchor <- new coordinate +/// enable_spend <- 1 (constant) +/// enable_output <- 1 (constant) +/// split_flag <- 0 (constant) +/// is_zatoshi_asset <- 1 (constant) +/// +/// Second row +/// asset_x <- zatoshi_asset.x() +/// asset_y <- zatoshi_asset.y() +/// diff_asset_x_inv <- 0 (constant) +/// diff_asset_y_inv <- 0 (constant) +/// psi_old <- 0 (constant) +/// psi_nf <- 0 (constant) +/// enable_zsa <- 1 (constant) +/// disable_cross_address <- disableCrossAddress +/// ``` +/// +/// With this layout, the gate constraints become: +/// +/// ```text +/// split_flag * (1 - split_flag) = 0 +/// -> 0 * (1-0) = 0 +/// v_old * (1 - split_flag) - v_new = magnitude * sign +/// -> disableCrossAddress * (1-0) - 0 = disableCrossAddress * 1 +/// (v_old + 1 - is_zatoshi_asset) * (root - anchor) = 0 +/// -> (disableCrossAddress + 1 - 1) * (old_coord - new_coord) = 0 +/// v_old * (1 - enable_spend) = 0 +/// -> disableCrossAddress * (1 - 1) = 0 +/// v_new * (1 - enable_output) = 0 +/// -> 0 * (1 - 1) = 0 +/// is_zatoshi_asset * (1 - is_zatoshi_asset) = 0 +/// -> 1 * (1-1) = 0 +/// is_zatoshi_asset * diff_asset_x = 0 +/// -> 1 * (zatoshi_asset.x() - zatoshi_asset.x()) = 0 +/// is_zatoshi_asset * diff_asset_y = 0 +/// -> 1 * (zatoshi_asset_y() - zatoshi_asset.y()) = 0 +/// (1 - is_zatoshi_asset) * (diff_asset_x * diff_asset_x_inv - 1) * (diff_asset_y * diff_asset_y_inv - 1) = 0 +/// -> (1 - 1) * (diff_asset_x * 0 - 1) * (diff_asset_y * 0 - 1) = 0 +/// (1 - split_flag)*(psi_old - psi_nf) = 0 +/// -> (1 - 0) * (0 - 0) = 0 +/// split_flag * is_zatoshi_asset = 0 +/// -> 0 * 1 = 0 +/// (1 - enable_zsa) * (1 - is_zatoshi_asset) = 0 +/// -> (1 - 1) * (1 - 1) = 0 +/// disable_cross_address * split_flag = 0 +/// -> disableCrossAddress * 0 = 0 +/// ``` +/// +/// The second line is the actual cross-address check. Any nonzero +/// `disableCrossAddress` value forces each old coordinate to equal the +/// corresponding new coordinate. The public API encodes `disableCrossAddress` +/// as 0 or 1, but this algebra does not rely on a boolean constraint. +fn synthesize_cross_address_checks( + config: &Config, + layouter: &mut impl Layouter, + addrs: &AddressPoints, +) -> Result<(), plonk::Error> { + let AddressPoints { + g_d_old, + pk_d_old, + g_d_new, + pk_d_new, + } = addrs; + + layouter.assign_region( + || "ZSA cross-address checks", + |mut region| { + let coordinate_checks = [ + ("g_d x", g_d_old.inner().x(), g_d_new.inner().x()), + ("g_d y", g_d_old.inner().y(), g_d_new.inner().y()), + ("pk_d x", pk_d_old.inner().x(), pk_d_new.inner().x()), + ("pk_d y", pk_d_old.inner().y(), pk_d_new.inner().y()), + ]; + + let mut offset = 0; + + for (label, old_coord, new_coord) in coordinate_checks.into_iter() { + config.q_orchard.enable(&mut region, offset)?; + + // Copy disableCrossAddress from the public input at + // primary[DISABLE_CROSS_ADDRESS] into advices[0] for this + // coordinate-check row. + let cross_address_disabled = region.assign_advice_from_instance( + || "v_old <- disableCrossAddress", + config.primary, + DISABLE_CROSS_ADDRESS, + config.advices[0], + offset, + )?; + + // Fill the v_new, magnitude, sign and split_flag cells so the reused + // value-balance constraint reads: + // disableCrossAddress * (1-0) - 0 = disableCrossAddress * 1. + region.assign_advice_from_constant( + || "v_new <- zero", + config.advices[1], + offset, + pallas::Base::zero(), + )?; + cross_address_disabled.copy_advice( + || "magnitude <- disableCrossAddress", + &mut region, + config.advices[2], + offset, + )?; + region.assign_advice_from_constant( + || "sign <- one", + config.advices[3], + offset, + pallas::Base::one(), + )?; + region.assign_advice_from_constant( + || "split_flag <- zero", + config.advices[8], + offset, + pallas::Base::zero(), + )?; + + // Copy the old coordinate into the gate's root cell and the + // new coordinate into its anchor cell for the equality check. + old_coord.copy_advice( + || format!("root <- {label}"), + &mut region, + config.advices[4], + offset, + )?; + new_coord.copy_advice( + || format!("anchor <- {label}"), + &mut region, + config.advices[5], + offset, + )?; + + // Set both enable flags to one so the unrelated enable checks + // in q_orchard are neutralized on these rows. + region.assign_advice_from_constant( + || "one (neutralize enable_spend check)", + config.advices[6], + offset, + pallas::Base::one(), + )?; + region.assign_advice_from_constant( + || "one (neutralize enable_output check)", + config.advices[7], + offset, + pallas::Base::one(), + )?; + + // Set `is_zatoshi_asset` to 1. + region.assign_advice_from_constant( + || "is_zatoshi_asset <- 1", + config.advices[9], + offset, + pallas::Base::one(), + )?; + + // Second row + offset += 1; + + // Set `(asset_x, asset_y) = AssetBase::zatoshi()`. + let zatoshi_asset = AssetBase::zatoshi() + .cv_base() + .to_affine() + .coordinates() + .unwrap(); + region.assign_advice_from_constant( + || "asset_x <- zatoshi_asset.x()", + config.advices[0], + offset, + *zatoshi_asset.x(), + )?; + region.assign_advice_from_constant( + || "asset_y <- zatoshi_asset.y()", + config.advices[1], + offset, + *zatoshi_asset.y(), + )?; + + // Set `diff_asset_x_inv` and `diff_asset_y_inv` to 0. + region.assign_advice_from_constant( + || "diff_asset_x_inv <- 0", + config.advices[2], + offset, + pallas::Base::zero(), + )?; + region.assign_advice_from_constant( + || "diff_asset_y_inv <- 0", + config.advices[3], + offset, + pallas::Base::zero(), + )?; + + // Set `psi_old` and `psi_nf` to 0 to satisfy the following constraint (split_flag=0): + // (1 - split_flag)*(psi_old - psi_nf) = 0 + region.assign_advice_from_constant( + || "psi_old <- 0", + config.advices[4], + offset, + pallas::Base::zero(), + )?; + region.assign_advice_from_constant( + || "psi_nf <- 0", + config.advices[5], + offset, + pallas::Base::zero(), + )?; + + // Set `enable_zsa` to 1 to satisfy the following constraint: + // (1 - enable_zsa) * (1 - is_zatoshi_asset) = 0 + region.assign_advice_from_constant( + || "enable_zsa <- 1", + config.advices[6], + offset, + pallas::Base::one(), + )?; + + cross_address_disabled.copy_advice( + || "disable_cross_address <- disableCrossAddress", + &mut region, + config.advices[7], + offset, + )?; + + // Occupy the otherwise-unused rightmost advice columns so the + // floor planner cannot lay out another region (and enable its + // gate) on these rows. + cross_address_disabled.copy_advice( + || "disableCrossAddress padding", + &mut region, + config.advices[8], + offset, + )?; + cross_address_disabled.copy_advice( + || "disableCrossAddress padding", + &mut region, + config.advices[9], + offset, + )?; + + offset += 1; + } + + Ok(()) + }, + ) +} + +// TODO ZSA: once ZSA bundle is implemented, extend the tests below to cover `split_flag = true`. + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + use core::iter; + + use ff::Field; + use group::GroupEncoding; + use halo2_proofs::{circuit::Value, dev::MockProver}; + use pasta_curves::pallas; + use rand::rngs::OsRng; + use rand_core::CryptoRngCore; + use subtle::Choice; + + use crate::{ + circuit::circuit_zsa::AdditionalZsaWitnesses, + circuit::{Circuit, CircuitVanilla, Instance, Proof, ProvingKey, VerifyingKey, K}, + circuit_version::OrchardCircuitVersion, + keys::{FullViewingKey, Scope, SpendValidatingKey, SpendingKey}, + note::{AssetBase, Note, NoteCommitment, NoteVersion, Nullifier, RandomSeed, Rho}, + tree::MerklePath, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + }; + + #[cfg(feature = "dev-graph")] + #[test] + fn print_action_circuit() { + use plotters::prelude::*; + + let root = + BitMapBackend::new("action-circuit-layout-zsa.png", (1024, 768)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root + .titled("Orchard Action Circuit", ("sans-serif", 60)) + .unwrap(); + + let circuit = crate::circuit::CircuitZsa::empty(); + halo2_proofs::dev::CircuitLayout::default() + .show_labels(false) + .view_height(0..(1 << 11)) + .render(K, &circuit, &root) + .unwrap(); + } + + /// Derives the note commitment for `note`, as if it carried `asset`. + /// + /// `Note` doesn't yet support constructing notes for a non-zatoshi `asset`, so callers + /// build an ordinary (zatoshi) `Note` for its other fields and use this to get a + /// commitment consistent with the ZSA-specific `asset` witness under test. + /// + /// TODO ZSA: once it is possible to construct a note with a non-zatoshi asset, remove + /// this function + fn note_commitment(note: &Note, asset: AssetBase) -> NoteCommitment { + NoteCommitment::derive( + note.recipient().g_d().to_bytes(), + note.recipient().pk_d().inner().to_bytes(), + note.value(), + asset, + note.rho().into_inner(), + note.psi(), + note.rcm(), + ) + .unwrap() + } + + /// Generates a circuit and instance whose output note is addressed to an expanded + /// receiver distinct from the spent note's. + fn generate_circuit_instance( + is_zatoshi_asset: bool, + rng: R, + ) -> (Circuit, Instance) { + generate_circuit_instance_inner(is_zatoshi_asset, false, false, rng) + } + + /// Generates a circuit and instance whose output note is addressed to the spent + /// note's expanded receiver, as the cross-address restriction requires. + fn generate_self_transfer_circuit_instance( + is_zatoshi_asset: bool, + rng: R, + ) -> (Circuit, Instance) { + generate_circuit_instance_inner(is_zatoshi_asset, true, false, rng) + } + + /// Generates a circuit and instance spending a split note. + /// + /// The asset is necessarily non-zatoshi: the circuit constrains + /// `(split_flag = 1) => (is_zatoshi_asset = 0)`. + fn generate_split_note_circuit_instance(rng: R) -> (Circuit, Instance) { + generate_circuit_instance_inner(false, false, true, rng) + } + + /// Extracts the contents of a `Value` that is known. + fn known(value: &Value) -> V { + let mut out = None; + value.clone().map(|v| out = Some(v)); + out.expect("value is known") + } + + fn generate_circuit_instance_inner( + is_zatoshi_asset: bool, + output_matches_spend: bool, + split_flag: bool, + mut rng: R, + ) -> (Circuit, Instance) { + // TODO ZSA: note_version should be NoteVersion::ZSA, once that variant exists + // (`NoteVersion` currently only has `V2`/`V3`). + let note_version = NoteVersion::V3; + + // Create asset + let asset_base = if is_zatoshi_asset { + AssetBase::zatoshi() + } else { + AssetBase::random(&mut rng) + }; + + let sk = SpendingKey::random(&mut rng); + let fvk: FullViewingKey = (&sk).into(); + let sender_address = fvk.address_at(0u32, Scope::External); + let nk = *fvk.nk(); + let rivk = fvk.rivk(fvk.scope_for_address(&sender_address).unwrap()); + let ak: SpendValidatingKey = fvk.into(); + let alpha = pallas::Scalar::random(&mut rng); + let rk = ak.randomize(&alpha); + + let rho_old = Rho::from_nf_old(Nullifier::dummy(&mut rng)); + let spent_note = Note::new( + sender_address, + NoteValue::from_raw(7), + rho_old, + note_version, + &mut rng, + ); + // A split note's nullifier is derived from a separate rseed, so `psi_nf` differs from + // `psi_old`. For a regular note the circuit constrains the two to be equal. + let psi_nf = if split_flag { + RandomSeed::random(&mut rng, &rho_old).psi(&rho_old) + } else { + spent_note.psi() + }; + + let cm_old = note_commitment(&spent_note, asset_base); + let nf_old = Nullifier::derive( + &nk, + spent_note.rho().into_inner(), + psi_nf, + cm_old.clone(), + Choice::from(u8::from(split_flag)), + ); + + let output_address = if output_matches_spend { + sender_address + } else { + loop { + let other_sk = SpendingKey::random(&mut rng); + let other_fvk: FullViewingKey = (&other_sk).into(); + let candidate = other_fvk.address_at(0u32, Scope::External); + if !sender_address.same_expanded_receiver(&candidate) { + break candidate; + } + } + }; + let output_note = Note::new( + output_address, + NoteValue::from_raw(3), + Rho::from_nf_old(nf_old), + note_version, + &mut rng, + ); + let cmx = note_commitment(&output_note, asset_base).into(); + + // A split note contributes no value to the action: v_net = -v_new. + let value = if split_flag { + NoteValue::ZERO - output_note.value() + } else { + spent_note.value() - output_note.value() + }; + let rcv = ValueCommitTrapdoor::random(&mut rng); + let cv_net = ValueCommitment::derive_with_asset(value, rcv.clone(), asset_base); + + let path = MerklePath::dummy(&mut rng); + let anchor = path.root(cm_old.clone().into()); + + ( + Circuit { + common_witnesses: CircuitVanilla { + circuit_version: OrchardCircuitVersion::ZSA, + path: Value::known(path.auth_path()), + pos: Value::known(path.position()), + g_d_old: Value::known(sender_address.g_d()), + pk_d_old: Value::known(*sender_address.pk_d()), + v_old: Value::known(spent_note.value()), + rho_old: Value::known(spent_note.rho()), + psi_old: Value::known(spent_note.psi()), + rcm_old: Value::known(spent_note.rcm()), + cm_old: Value::known(cm_old), + alpha: Value::known(alpha), + ak: Value::known(ak), + nk: Value::known(nk), + rivk: Value::known(rivk), + g_d_new: Value::known(output_note.recipient().g_d()), + pk_d_new: Value::known(*output_note.recipient().pk_d()), + v_new: Value::known(output_note.value()), + psi_new: Value::known(output_note.psi()), + rcm_new: Value::known(output_note.rcm()), + rcv: Value::known(rcv), + }, + additional_zsa_witnesses: Value::known(AdditionalZsaWitnesses { + psi_nf, + asset: asset_base, + split_flag, + }), + }, + Instance { + anchor, + cv_net, + nf_old, + rk, + cmx, + enable_spend: true, + enable_output: true, + cross_address_disabled: false, + enable_zsa: true, + }, + ) + } + + /// Runs `MockProver` on the ZSA circuit for `circuit`/`instance`. + fn zsa_mock_verify( + circuit: &Circuit, + instance: &Instance, + ) -> Result<(), Vec> { + MockProver::run( + K, + &circuit.to_zsa(), + instance + .to_halo2_instance() + .iter() + .map(|p| p.to_vec()) + .collect(), + ) + .unwrap() + .verify() + } + + /// Asserts that the statement is rejected, and *only* because of `constraint`. + fn assert_zsa_rejected_by(circuit: &Circuit, instance: &Instance, constraint: &str) { + let failures = zsa_mock_verify(circuit, instance).expect_err("statement must be rejected"); + let unrelated: Vec<_> = failures + .iter() + .map(|failure| alloc::format!("{failure}")) + .filter(|failure| !failure.contains(constraint)) + .collect(); + assert!( + unrelated.is_empty(), + "expected only `{constraint}` to fail, but also got: {unrelated:#?}" + ); + } + + #[test] + fn zsa_mock_prover_zatoshi_asset() { + let (circuit, instance) = generate_circuit_instance(true, OsRng); + assert_eq!(zsa_mock_verify(&circuit, &instance), Ok(())); + } + + #[test] + fn zsa_mock_prover_non_zatoshi_asset() { + let (circuit, instance) = generate_circuit_instance(false, OsRng); + assert_eq!(zsa_mock_verify(&circuit, &instance), Ok(())); + } + + #[test] + fn zsa_cross_address_restriction_is_conditional() { + // An unrestricted cross-address statement is satisfiable... + let (circuit, mut instance) = generate_circuit_instance(false, OsRng); + assert_eq!(zsa_mock_verify(&circuit, &instance), Ok(())); + + // ...but setting `disableCrossAddress` makes it unsatisfiable... + instance.cross_address_disabled = true; + assert!(zsa_mock_verify(&circuit, &instance).is_err()); + + // ...while a restricted self-transfer statement is satisfiable. + let (circuit, mut instance) = generate_self_transfer_circuit_instance(false, OsRng); + instance.cross_address_disabled = true; + assert_eq!(zsa_mock_verify(&circuit, &instance), Ok(())); + } + + #[test] + fn zsa_mock_prover_split_note() { + let (circuit, instance) = generate_split_note_circuit_instance(OsRng); + assert_eq!(zsa_mock_verify(&circuit, &instance), Ok(())); + } + + #[test] + fn zsa_mock_prover_rejects_non_split_nullifier_for_split_note() { + let (circuit, mut instance) = generate_split_note_circuit_instance(OsRng); + + let psi_nf = known(&circuit.additional_zsa_witnesses).psi_nf; + let cm_old = known(&circuit.common_witnesses.cm_old); + let nk = known(&circuit.common_witnesses.nk); + let rho_old = known(&circuit.common_witnesses.rho_old); + + // Replace the public nullifier by the one this note would have if it were not a split note. + instance.nf_old = + Nullifier::derive(&nk, rho_old.into_inner(), psi_nf, cm_old, Choice::from(0)); + + // The nullifier the circuit derives failing to equal the public one. + assert_zsa_rejected_by(&circuit, &instance, "Equality constraint not satisfied"); + } + + #[test] + fn zsa_mock_prover_rejects_split_note_with_zatoshi_asset() { + // The circuit constrains `(split_flag = 1) => (is_zatoshi_asset = 0)`. + let (circuit, instance) = generate_circuit_instance_inner(true, false, true, OsRng); + assert_zsa_rejected_by( + &circuit, + &instance, + "(split_flag = 1) => (is_zatoshi_asset = 0)", + ); + } + + #[test] + fn zsa_mock_prover_rejects_split_note_with_cross_address_disabled() { + // The circuit constrains `(disable_cross_address = 1) => (split_flag = 0)`. + let (circuit, mut instance) = generate_circuit_instance_inner(false, true, true, OsRng); + instance.cross_address_disabled = true; + assert_zsa_rejected_by( + &circuit, + &instance, + "(disable_cross_address = 1) => (split_flag = 0)", + ); + } + + #[test] + fn zsa_mock_prover_rejects_asset_mismatch() { + // The `asset` witness must match the asset baked into `cm_old`/`cv_net`. + let mut rng = OsRng; + let (mut circuit, instance) = generate_circuit_instance(false, &mut rng); + circuit.additional_zsa_witnesses = circuit.additional_zsa_witnesses.map(|mut w| { + let current_asset = w.asset; + let another_asset = loop { + let candidate = AssetBase::random(&mut rng); + if candidate != current_asset { + break candidate; + } + }; + w.asset = another_asset; + w + }); + // The asset is bound into `cm_old`, `cmx` and `cv_net`, so a lie about it is caught by + // those equalities rather than by a gate constraint. + assert_zsa_rejected_by(&circuit, &instance, "Equality constraint not satisfied"); + } + + #[test] + fn zsa_mock_prover_rejects_enable_zsa_false_for_non_zatoshi_asset() { + // The circuit constrains `(enable_zsa = 0) => (is_zatoshi_asset = 1)`. + let (circuit, mut instance) = generate_circuit_instance(false, OsRng); + instance.enable_zsa = false; + assert_zsa_rejected_by( + &circuit, + &instance, + "(enable_zsa = 0) => (is_zatoshi_asset = 1)", + ); + } + + // Set ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF to regenerate the pinned circuit description. + #[test] + fn zsa_pinned_circuit_description() { + let vk = VerifyingKey::build(OrchardCircuitVersion::ZSA); + + if std::env::var_os("ORCHARD_CIRCUIT_TEST_GENERATE_NEW_PROOF").is_some() { + std::fs::write( + "src/circuit_data/circuit_description_zsa", + format!("{:#?}\n", vk.vk.pinned()), + ) + .expect("should be able to write new circuit description"); + } else { + assert_eq!( + format!("{:#?}\n", vk.vk.pinned()), + include_str!("../circuit_data/circuit_description_zsa").replace("\r\n", "\n") + ); + } + } + + #[test] + fn zsa_prove_and_verify() { + let mut rng = OsRng; + let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) + .map(|()| generate_circuit_instance(false, &mut rng)) + .unzip(); + + let vk = VerifyingKey::build(OrchardCircuitVersion::ZSA); + + // Test that the proof size is as expected. + let expected_proof_size = { + let circuit_cost = + halo2_proofs::dev::CircuitCost::::measure( + K, + &circuits[0].to_zsa(), + ); + assert_eq!(usize::from(circuit_cost.proof_size(1)), 5120); + assert_eq!(usize::from(circuit_cost.proof_size(2)), 7392); + // The constants in `Proof::expected_proof_size` must track the circuit's actual + // proof size; this guards them against drift if the circuit ever changes. + assert_eq!( + Proof::expected_proof_size(OrchardCircuitVersion::ZSA, 1), + 5120 + ); + assert_eq!( + Proof::expected_proof_size(OrchardCircuitVersion::ZSA, 2), + 7392 + ); + assert_eq!( + Proof::expected_proof_size(OrchardCircuitVersion::ZSA, instances.len()), + usize::from(circuit_cost.proof_size(instances.len())), + ); + usize::from(circuit_cost.proof_size(instances.len())) + }; + + for (circuit, instance) in circuits.iter().zip(instances.iter()) { + assert_eq!(zsa_mock_verify(circuit, instance), Ok(())); + } + + let pk = ProvingKey::build(OrchardCircuitVersion::ZSA); + let proof = Proof::create(&pk, &circuits, &instances, &mut rng).unwrap(); + assert!(proof.verify(&vk, &instances).is_ok()); + assert_eq!(proof.0.len(), expected_proof_size); + } +} diff --git a/src/circuit/commit_ivk.rs b/src/circuit/commit_ivk.rs index 604fd90ad..369322000 100644 --- a/src/circuit/commit_ivk.rs +++ b/src/circuit/commit_ivk.rs @@ -9,7 +9,7 @@ use core::iter; use group::ff::{Field, PrimeField}; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Value}, + circuit::{AssignedCell, Chip, Layouter, Value}, plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, poly::Rotation, }; @@ -243,11 +243,7 @@ impl CommitIvkChip { /// Gadget functions for `CommitIvk` operations. #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] pub(in crate::circuit) mod gadgets { - use halo2_gadgets::utilities::{ - lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, - RangeConstrained, - }; - use halo2_proofs::circuit::Chip; + use halo2_gadgets::utilities::{lookup_range_check::PallasLookupRangeCheck, RangeConstrained}; use super::*; @@ -257,19 +253,20 @@ pub(in crate::circuit) mod gadgets { #[allow(non_snake_case)] #[allow(clippy::type_complexity)] #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] - pub(in crate::circuit) fn commit_ivk( + pub(in crate::circuit) fn commit_ivk( sinsemilla_chip: SinsemillaChip< OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases, + Lookup, >, - ecc_chip: EccChip, + ecc_chip: EccChip, commit_ivk_chip: CommitIvkChip, mut layouter: impl Layouter, ak: AssignedCell, nk: AssignedCell, - rivk: ScalarFixed>, - ) -> Result>, Error> { + rivk: ScalarFixed>, + ) -> Result>, Error> { let lookup_config = sinsemilla_chip.config().lookup_config(); // We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits). @@ -417,8 +414,8 @@ pub(in crate::circuit) mod gadgets { /// /// [Specification](https://p.z.cash/orchard-0.1:commit-ivk-canonicity-ak?partial). #[allow(clippy::type_complexity)] - fn ak_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn ak_canonicity( + lookup_config: &Lookup, mut layouter: impl Layouter, a: AssignedCell, ) -> Result< @@ -458,8 +455,8 @@ pub(in crate::circuit) mod gadgets { /// /// [Specification](https://p.z.cash/orchard-0.1:commit-ivk-canonicity-nk?partial). #[allow(clippy::type_complexity)] - fn nk_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn nk_canonicity( + lookup_config: &Lookup, mut layouter: impl Layouter, b_2: &RangeConstrained>, c: AssignedCell, @@ -698,7 +695,10 @@ mod tests { primitives::CommitDomain, }, utilities::{ - lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, + lookup_range_check::{ + PallasLookupRangeCheck, PallasLookupRangeCheck4_5BConfig, + PallasLookupRangeCheckConfig, + }, UtilitiesInstructions, }, }; @@ -710,210 +710,236 @@ mod tests { use pasta_curves::pallas; use rand::rngs::OsRng; - #[test] - fn commit_ivk() { - #[derive(Default)] - struct MyCircuit { - ak: Value, - nk: Value, - } + trait MyLookup: PallasLookupRangeCheck { + const ENABLE_HASH_FROM_PRIVATE_POINT: bool; + } + + impl MyLookup for PallasLookupRangeCheckConfig { + const ENABLE_HASH_FROM_PRIVATE_POINT: bool = false; + } + + impl MyLookup for PallasLookupRangeCheck4_5BConfig { + const ENABLE_HASH_FROM_PRIVATE_POINT: bool = true; + } - impl UtilitiesInstructions for MyCircuit { - type Var = AssignedCell; + #[derive(Default)] + struct MyCircuit { + ak: Value, + nk: Value, + phantom: core::marker::PhantomData, + } + + impl UtilitiesInstructions for MyCircuit { + type Var = AssignedCell; + } + + impl Circuit for MyCircuit { + type Config = ( + SinsemillaConfig, + CommitIvkConfig, + EccConfig, + ); + + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { + ak: Default::default(), + nk: Default::default(), + phantom: core::marker::PhantomData, + } } - impl Circuit for MyCircuit { - type Config = ( - SinsemillaConfig, - CommitIvkConfig, - EccConfig, + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + let constants = meta.fixed_column(); + meta.enable_constant(constants); + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + let table_idx = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + ); + let lagrange_coeffs = [ + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ]; + + let range_check = Lookup::configure(meta, advices[9], table_idx); + let sinsemilla_config = SinsemillaChip::< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + Lookup, + >::configure( + meta, + advices[..5].try_into().unwrap(), + advices[2], + lagrange_coeffs[0], + lookup, + range_check, + Lookup::ENABLE_HASH_FROM_PRIVATE_POINT, ); - type FloorPlanner = SimpleFloorPlanner; - fn without_witnesses(&self) -> Self { - Self::default() - } + let commit_ivk_config = CommitIvkChip::configure(meta, advices); - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let advices = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; - - let constants = meta.fixed_column(); - meta.enable_constant(constants); - - for advice in advices.iter() { - meta.enable_equality(*advice); - } + let ecc_config = EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ); - let table_idx = meta.lookup_table_column(); - let lookup = ( - table_idx, - meta.lookup_table_column(), - meta.lookup_table_column(), - ); - let lagrange_coeffs = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - - let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx); - let sinsemilla_config = SinsemillaChip::< + (sinsemilla_config, commit_ivk_config, ecc_config) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (sinsemilla_config, commit_ivk_config, ecc_config) = config; + + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::< OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases, - >::configure( - meta, - advices[..5].try_into().unwrap(), - advices[2], - lagrange_coeffs[0], - lookup, - range_check, - false, - ); - - let commit_ivk_config = CommitIvkChip::configure(meta, advices); - - let ecc_config = EccChip::::configure( - meta, - advices, - lagrange_coeffs, - range_check, - ); - - (sinsemilla_config, commit_ivk_config, ecc_config) - } + Lookup, + >::load(sinsemilla_config.clone(), &mut layouter)?; - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let (sinsemilla_config, commit_ivk_config, ecc_config) = config; - - // Load the Sinsemilla generator lookup table used by the whole circuit. - SinsemillaChip::::load(sinsemilla_config.clone(), &mut layouter)?; - - // Construct a Sinsemilla chip - let sinsemilla_chip = SinsemillaChip::construct(sinsemilla_config); - - // Construct an ECC chip - let ecc_chip = EccChip::construct(ecc_config, CircuitVersion::AnchoredBase); - - let commit_ivk_chip = CommitIvkChip::construct(commit_ivk_config.clone()); - - // Witness ak - let ak = self.load_private( - layouter.namespace(|| "load ak"), - commit_ivk_config.advices[0], - self.ak, - )?; - - // Witness nk - let nk = self.load_private( - layouter.namespace(|| "load nk"), - commit_ivk_config.advices[0], - self.nk, - )?; - - // Use a random scalar for rivk - let rivk = pallas::Scalar::random(OsRng); - let rivk_gadget = ScalarFixed::new( - ecc_chip.clone(), - layouter.namespace(|| "rivk"), - Value::known(rivk), - )?; - - let ivk = gadgets::commit_ivk( - sinsemilla_chip, - ecc_chip, - commit_ivk_chip, - layouter.namespace(|| "CommitIvk"), - ak, - nk, - rivk_gadget, - )?; - - self.ak - .zip(self.nk) - .zip(ivk.inner().value()) - .assert_if_known(|((ak, nk), ivk)| { - let expected_ivk = { - let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION); - // Hash ak || nk - domain - .short_commit( - iter::empty() - .chain( - ak.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE), - ) - .chain( - nk.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE), - ), - &rivk, - ) - .unwrap() - }; - - &&expected_ivk == ivk - }); + // Construct a Sinsemilla chip + let sinsemilla_chip = SinsemillaChip::construct(sinsemilla_config); - Ok(()) - } + // Construct an ECC chip + let ecc_chip = EccChip::construct(ecc_config, CircuitVersion::AnchoredBase); + + let commit_ivk_chip = CommitIvkChip::construct(commit_ivk_config.clone()); + + // Witness ak + let ak = self.load_private( + layouter.namespace(|| "load ak"), + commit_ivk_config.advices[0], + self.ak, + )?; + + // Witness nk + let nk = self.load_private( + layouter.namespace(|| "load nk"), + commit_ivk_config.advices[0], + self.nk, + )?; + + // Use a random scalar for rivk + let rivk = pallas::Scalar::random(OsRng); + let rivk_gadget = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rivk"), + Value::known(rivk), + )?; + + let ivk = gadgets::commit_ivk( + sinsemilla_chip, + ecc_chip, + commit_ivk_chip, + layouter.namespace(|| "CommitIvk"), + ak, + nk, + rivk_gadget, + )?; + + self.ak + .zip(self.nk) + .zip(ivk.inner().value()) + .assert_if_known(|((ak, nk), ivk)| { + let expected_ivk = { + let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION); + // Hash ak || nk + domain + .short_commit( + iter::empty() + .chain(ak.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)) + .chain(nk.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)), + &rivk, + ) + .unwrap() + }; + + &&expected_ivk == ivk + }); + + Ok(()) } + } + fn commit_ivk() { let two_pow_254 = pallas::Base::from_u128(1 << 127).square(); // Test different values of `ak`, `nk` let circuits = [ // `ak` = 0, `nk` = 0 - MyCircuit { + MyCircuit:: { ak: Value::known(pallas::Base::zero()), nk: Value::known(pallas::Base::zero()), + phantom: core::marker::PhantomData, }, // `ak` = T_Q - 1, `nk` = T_Q - 1 - MyCircuit { + MyCircuit:: { ak: Value::known(pallas::Base::from_u128(T_Q - 1)), nk: Value::known(pallas::Base::from_u128(T_Q - 1)), + phantom: core::marker::PhantomData, }, // `ak` = T_Q, `nk` = T_Q - MyCircuit { + MyCircuit:: { ak: Value::known(pallas::Base::from_u128(T_Q)), nk: Value::known(pallas::Base::from_u128(T_Q)), + phantom: core::marker::PhantomData, }, // `ak` = 2^127 - 1, `nk` = 2^127 - 1 - MyCircuit { + MyCircuit:: { ak: Value::known(pallas::Base::from_u128((1 << 127) - 1)), nk: Value::known(pallas::Base::from_u128((1 << 127) - 1)), + phantom: core::marker::PhantomData, }, // `ak` = 2^127, `nk` = 2^127 - MyCircuit { + MyCircuit:: { ak: Value::known(pallas::Base::from_u128(1 << 127)), nk: Value::known(pallas::Base::from_u128(1 << 127)), + phantom: core::marker::PhantomData, }, // `ak` = 2^254 - 1, `nk` = 2^254 - 1 - MyCircuit { + MyCircuit:: { ak: Value::known(two_pow_254 - pallas::Base::one()), nk: Value::known(two_pow_254 - pallas::Base::one()), + phantom: core::marker::PhantomData, }, // `ak` = 2^254, `nk` = 2^254 - MyCircuit { + MyCircuit:: { ak: Value::known(two_pow_254), nk: Value::known(two_pow_254), + phantom: core::marker::PhantomData, }, ]; @@ -922,4 +948,14 @@ mod tests { assert_eq!(prover.verify(), Ok(())); } } + + #[test] + fn commit_ivk_vanilla() { + commit_ivk::() + } + + #[test] + fn commit_ivk_zsa() { + commit_ivk::() + } } diff --git a/src/circuit/derive_nullifier.rs b/src/circuit/derive_nullifier.rs new file mode 100644 index 000000000..46cdf87e7 --- /dev/null +++ b/src/circuit/derive_nullifier.rs @@ -0,0 +1,385 @@ +//! Derive nullifier logic for the Orchard circuit. + +use crate::constants::nullifier_l::nullifier_l; +use halo2_gadgets::utilities::cond_swap::CondSwapChip; +use halo2_proofs::circuit::AssignedCell; +use pasta_curves::pallas; + +/// Parameters needed to derive a nullifier for a split note. +#[derive(Debug)] +#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] +pub(super) struct ZsaNullifierParams { + pub(super) cond_swap_chip: CondSwapChip, + pub(super) split_flag: AssignedCell, +} + +/// Gadget functions for `DeriveNullifier` operations. +#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] +pub(in crate::circuit) mod gadgets { + use super::*; + + use crate::{ + circuit::gadget::AddInstruction, + constants::{NullifierK, OrchardFixedBases}, + }; + use halo2_gadgets::{ + ecc::{chip::EccPoint, EccInstructions, FixedPointBaseField, Point, X}, + poseidon::{ + primitives::{self as poseidon, ConstantLength}, + Hash as PoseidonHash, PoseidonSpongeInstructions, + }, + }; + use halo2_proofs::{circuit::Layouter, plonk}; + + /// `DeriveNullifier` from [Section 4.16: Note Commitments and Nullifiers]. + /// + /// [Section 4.16: Note Commitments and Nullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers + #[allow(clippy::too_many_arguments)] + #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] + pub(in crate::circuit) fn derive_nullifier< + PoseidonChip: PoseidonSpongeInstructions, 3, 2>, + AddChip: AddInstruction, + EccChip: EccInstructions< + pallas::Affine, + FixedPoints = OrchardFixedBases, + Point = EccPoint, + Var = AssignedCell, + >, + >( + mut layouter: impl Layouter, + poseidon_chip: PoseidonChip, + add_chip: AddChip, + ecc_chip: EccChip, + rho: AssignedCell, + psi: &AssignedCell, + cm: &Point, + nk: AssignedCell, + zsa_params: Option, + ) -> Result, plonk::Error> { + // hash = poseidon_hash(nk, rho) + let hash = { + let poseidon_message = [nk, rho]; + let poseidon_hasher = + PoseidonHash::init(poseidon_chip, layouter.namespace(|| "Poseidon init"))?; + poseidon_hasher.hash( + layouter.namespace(|| "Poseidon hash (nk, rho)"), + poseidon_message, + )? + }; + + // Add hash output to psi. + // `scalar` = poseidon_hash(nk, rho) + psi. + let scalar = add_chip.add( + layouter.namespace(|| "scalar = poseidon_hash(nk, rho) + psi"), + &hash, + psi, + )?; + + // Multiply scalar by NullifierK + // `product` = [poseidon_hash(nk, rho) + psi] NullifierK. + let product = { + let nullifier_k = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK.into()); + nullifier_k.mul( + layouter.namespace(|| "[poseidon_output + psi] NullifierK"), + scalar, + )? + }; + + // Add cm to multiplied fixed base + // nf = cm + [poseidon_output + psi] NullifierK + let nf = cm.add(layouter.namespace(|| "nf"), &product)?; + + match zsa_params { + None => Ok(nf.extract_p()), + Some(zsa_params) => { + // Add NullifierL to nf + // split_note_nf = NullifierL + nf + let nullifier_l = Point::new_from_constant( + ecc_chip.clone(), + layouter.namespace(|| "witness NullifierL constant"), + nullifier_l(), + )?; + let split_note_nf = nullifier_l.add(layouter.namespace(|| "split_note_nf"), &nf)?; + + // Select the desired nullifier according to split_flag + Ok(Point::from_inner( + ecc_chip, + zsa_params.cond_swap_chip.mux_on_points( + layouter.namespace(|| "mux on nf"), + &zsa_params.split_flag, + nf.inner(), + split_note_nf.inner(), + )?, + ) + .extract_p()) + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + circuit::{ + derive_nullifier::{gadgets::derive_nullifier, ZsaNullifierParams}, + gadget::{ + add_chip::{AddChip, AddConfig}, + assign_free_advice, assign_split_flag, + }, + K, + }, + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, + keys::NullifierDerivingKey, + note::{commitment::NoteCommitment, Note, NoteVersion, Nullifier}, + }; + use halo2_gadgets::{ + ecc::{ + chip::{CircuitVersion, EccChip, EccConfig}, + Point, + }, + poseidon::{primitives as poseidon, Pow5Chip, Pow5Config}, + sinsemilla::chip::{SinsemillaChip, SinsemillaConfig}, + utilities::{ + cond_swap::{CondSwapChip, CondSwapConfig}, + lookup_range_check::{LookupRangeCheck4_5BConfig, PallasLookupRangeCheck4_5BConfig}, + }, + }; + + use group::Curve; + use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::MockProver, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance}, + }; + use pasta_curves::pallas; + use rand::rngs::OsRng; + use subtle::Choice; + + /// Checks that the `derive_nullifier` gadget agrees with `Nullifier::derive`. + #[test] + fn test_derive_nullifier() { + #[derive(Clone, Debug)] + struct MyConfig { + primary: Column, + advices: [Column; 10], + add_config: AddConfig, + ecc_config: EccConfig, + poseidon_config: Pow5Config, + cond_swap_config: CondSwapConfig, + // The Sinsemilla config is only used to initialize the table_idx lookup table in + // the same way as in the Orchard circuit. + sinsemilla_config: SinsemillaConfig< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + PallasLookupRangeCheck4_5BConfig, + >, + } + + #[derive(Default)] + struct MyCircuit { + nk: Value, + rho: Value, + psi: Value, + cm: Value, + // When `Some`, call the gadget with `ZsaNullifierParams` carrying this split_flag. + split_flag: Option, + } + + impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { + split_flag: self.split_flag, + ..Default::default() + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + let primary = meta.instance_column(); + meta.enable_equality(primary); + + let table_idx = meta.lookup_table_column(); + let table_range_check_tag = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + ); + + let lagrange_coeffs = [ + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ]; + meta.enable_constant(lagrange_coeffs[0]); + + let range_check = LookupRangeCheck4_5BConfig::configure_with_tag( + meta, + advices[9], + table_idx, + table_range_check_tag, + ); + + let sinsemilla_config = SinsemillaChip::configure( + meta, + advices[..5].try_into().unwrap(), + advices[6], + lagrange_coeffs[0], + lookup, + range_check, + true, + ); + + // Laid out exactly as in the Orchard circuit. + let add_config = AddChip::configure(meta, advices[7], advices[8], advices[6]); + let poseidon_config = Pow5Chip::configure::( + meta, + advices[6..9].try_into().unwrap(), + advices[5], + lagrange_coeffs[2..5].try_into().unwrap(), + lagrange_coeffs[5..8].try_into().unwrap(), + ); + let cond_swap_config = + CondSwapChip::configure(meta, advices[..5].try_into().unwrap()); + + MyConfig { + primary, + advices, + add_config, + ecc_config: + EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ), + poseidon_config, + cond_swap_config, + sinsemilla_config, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + // Load the Sinsemilla generator lookup table. + SinsemillaChip::load(config.sinsemilla_config.clone(), &mut layouter)?; + + let ecc_chip = EccChip::construct(config.ecc_config, CircuitVersion::AnchoredBase); + + let nk = assign_free_advice( + layouter.namespace(|| "witness nk"), + config.advices[0], + self.nk.map(|nk| nk.inner()), + )?; + let rho = assign_free_advice( + layouter.namespace(|| "witness rho"), + config.advices[0], + self.rho, + )?; + let psi = assign_free_advice( + layouter.namespace(|| "witness psi"), + config.advices[0], + self.psi, + )?; + let cm = Point::new( + ecc_chip.clone(), + layouter.namespace(|| "witness cm"), + self.cm.as_ref().map(|cm| cm.inner().to_affine()), + )?; + + // Build the gadget's `zsa_params` from `self.split_flag`: + // - `None` -> `zsa_params = None` + // - `Some(false)` -> `zsa_params = Some(..)` with `split_flag = 0` + // - `Some(true)` -> `zsa_params = Some(..)` with `split_flag = 1` + let zsa_params = self + .split_flag + .map(|split_flag| { + let split_flag = assign_split_flag( + layouter.namespace(|| "witness split_flag"), + config.advices[0], + Value::known(split_flag), + )?; + Ok::<_, Error>(ZsaNullifierParams { + cond_swap_chip: CondSwapChip::construct( + config.cond_swap_config.clone(), + ), + split_flag, + }) + }) + .transpose()?; + + let nf = derive_nullifier( + layouter.namespace(|| "nf = DeriveNullifier_nk(rho, psi, cm)"), + Pow5Chip::construct(config.poseidon_config.clone()), + AddChip::construct(config.add_config.clone()), + ecc_chip, + rho, + &psi, + &cm, + nk, + zsa_params, + )?; + + layouter.constrain_instance(nf.inner().cell(), config.primary, 0) + } + } + + let mut rng = OsRng; + let (_, fvk, note) = Note::dummy(&mut rng, None, NoteVersion::V3); + + for split_flag in [None, Some(false), Some(true)] { + // `split_flag` drives both the circuit and the expected value: + // - `None` -> `zsa_params = None` + // - `Some(false)` -> `zsa_params = Some(..)` with `split_flag = 0` + // - `Some(true)` -> `zsa_params = Some(..)` with `split_flag = 1` + let expected_nf = Nullifier::derive( + fvk.nk(), + note.rho().into_inner(), + note.psi(), + note.commitment(), + Choice::from(u8::from(split_flag.unwrap_or(false))), + ); + + let circuit = MyCircuit { + nk: Value::known(*fvk.nk()), + rho: Value::known(note.rho().into_inner()), + psi: Value::known(note.psi()), + cm: Value::known(note.commitment()), + split_flag, + }; + + let prover = + MockProver::::run(K, &circuit, vec![vec![expected_nf.inner()]]) + .unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + } +} diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 9b131a8a1..a9d3e6115 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -1,23 +1,18 @@ -//! Gadgets used in the Orchard circuit. +//! Common gadgets and functions used in the Orchard circuit. use ff::Field; use pasta_curves::pallas; -use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip}; -use crate::constants::{ - NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, - ValueCommitV, +use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip, Config}; +use crate::{ + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, + note::AssetBase, }; use halo2_gadgets::{ - ecc::{ - chip::EccChip, CircuitVersion, EccInstructions, FixedPoint, FixedPointBaseField, - FixedPointShort, Point, ScalarFixed, ScalarFixedShort, X, - }, - poseidon::{ - primitives::{self as poseidon, ConstantLength}, - Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip, - }, + ecc::{chip::EccChip, CircuitVersion}, + poseidon::Pow5Chip as PoseidonChip, sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}, + utilities::{cond_swap::CondSwapChip, lookup_range_check::PallasLookupRangeCheck}, }; use halo2_proofs::{ circuit::{AssignedCell, Chip, Layouter, Value}, @@ -30,7 +25,7 @@ pub(in crate::circuit) mod add_chip; #[cfg(feature = "unstable-voting-circuits")] pub mod add_chip; -impl super::Config { +impl Config { pub(super) fn add_chip(&self) -> add_chip::AddChip { add_chip::AddChip::construct(self.add_config.clone()) } @@ -39,31 +34,34 @@ impl super::Config { CommitIvkChip::construct(self.commit_ivk_config.clone()) } - pub(super) fn ecc_chip(&self, circuit_version: CircuitVersion) -> EccChip { + pub(super) fn ecc_chip( + &self, + circuit_version: CircuitVersion, + ) -> EccChip { EccChip::construct(self.ecc_config.clone(), circuit_version) } pub(super) fn sinsemilla_chip_1( &self, - ) -> SinsemillaChip { + ) -> SinsemillaChip { SinsemillaChip::construct(self.sinsemilla_config_1.clone()) } pub(super) fn sinsemilla_chip_2( &self, - ) -> SinsemillaChip { + ) -> SinsemillaChip { SinsemillaChip::construct(self.sinsemilla_config_2.clone()) } pub(super) fn merkle_chip_1( &self, - ) -> MerkleChip { + ) -> MerkleChip { MerkleChip::construct(self.merkle_config_1.clone()) } pub(super) fn merkle_chip_2( &self, - ) -> MerkleChip { + ) -> MerkleChip { MerkleChip::construct(self.merkle_config_2.clone()) } @@ -71,13 +69,17 @@ impl super::Config { PoseidonChip::construct(self.poseidon_config.clone()) } - pub(super) fn note_commit_chip_new(&self) -> NoteCommitChip { + pub(super) fn note_commit_chip_new(&self) -> NoteCommitChip { NoteCommitChip::construct(self.new_note_commit_config.clone()) } - pub(super) fn note_commit_chip_old(&self) -> NoteCommitChip { + pub(super) fn note_commit_chip_old(&self) -> NoteCommitChip { NoteCommitChip::construct(self.old_note_commit_config.clone()) } + + pub(super) fn cond_swap_chip(&self) -> CondSwapChip { + CondSwapChip::construct(self.merkle_config_1.cond_swap_config().clone()) + } } /// An instruction set for adding two circuit words (field elements). @@ -112,100 +114,46 @@ where ) } -/// `ValueCommit^Orchard` from [Section 5.4.8.3 Homomorphic Pedersen commitments (Sapling and Orchard)]. -/// -/// [Section 5.4.8.3 Homomorphic Pedersen commitments (Sapling and Orchard)]: https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit -pub(in crate::circuit) fn value_commit_orchard< - EccChip: EccInstructions< - pallas::Affine, - FixedPoints = OrchardFixedBases, - Var = AssignedCell, - >, ->( - mut layouter: impl Layouter, - ecc_chip: EccChip, - v: ScalarFixedShort, - rcv: ScalarFixed, -) -> Result, plonk::Error> { - // commitment = [v] ValueCommitV - let (commitment, _) = { - let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), ValueCommitV.into()); - value_commit_v.mul(layouter.namespace(|| "[v] ValueCommitV"), v)? - }; - - // blind = [rcv] ValueCommitR - let (blind, _rcv) = { - let value_commit_r = OrchardFixedBasesFull::ValueCommitR; - let value_commit_r = FixedPoint::from_inner(ecc_chip, value_commit_r); - - // [rcv] ValueCommitR - value_commit_r.mul(layouter.namespace(|| "[rcv] ValueCommitR"), rcv)? - }; - - // [v] ValueCommitV + [rcv] ValueCommitR - commitment.add(layouter.namespace(|| "cv"), &blind) +/// Witnesses is_zatoshi_asset. +pub(in crate::circuit) fn assign_is_zatoshi_asset( + layouter: impl Layouter, + column: Column, + asset: Value, +) -> Result, plonk::Error> +where + Assigned: for<'v> From<&'v pasta_curves::Fp>, +{ + assign_free_advice( + layouter, + column, + asset.map(|asset| { + if bool::from(asset.is_zatoshi()) { + pallas::Base::one() + } else { + pallas::Base::zero() + } + }), + ) } -/// `DeriveNullifier` from [Section 4.16: Note Commitments and Nullifiers]. -/// -/// [Section 4.16: Note Commitments and Nullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers -#[allow(clippy::too_many_arguments)] -#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] -pub(in crate::circuit) fn derive_nullifier< - PoseidonChip: PoseidonSpongeInstructions, 3, 2>, - AddChip: AddInstruction, - EccChip: EccInstructions< - pallas::Affine, - FixedPoints = OrchardFixedBases, - Var = AssignedCell, - >, ->( - mut layouter: impl Layouter, - poseidon_chip: PoseidonChip, - add_chip: AddChip, - ecc_chip: EccChip, - rho: AssignedCell, - psi: &AssignedCell, - cm: &Point, - nk: AssignedCell, -) -> Result, plonk::Error> { - // hash = poseidon_hash(nk, rho) - let hash = { - let poseidon_message = [nk, rho]; - let poseidon_hasher = - PoseidonHash::init(poseidon_chip, layouter.namespace(|| "Poseidon init"))?; - poseidon_hasher.hash( - layouter.namespace(|| "Poseidon hash (nk, rho)"), - poseidon_message, - )? - }; - - // Add hash output to psi. - // `scalar` = poseidon_hash(nk, rho) + psi. - let scalar = add_chip.add( - layouter.namespace(|| "scalar = poseidon_hash(nk, rho) + psi"), - &hash, - psi, - )?; - - // Multiply scalar by NullifierK - // `product` = [poseidon_hash(nk, rho) + psi] NullifierK. - // - let product = { - let nullifier_k = FixedPointBaseField::from_inner(ecc_chip, NullifierK.into()); - nullifier_k.mul( - layouter.namespace(|| "[poseidon_output + psi] NullifierK"), - scalar, - )? - }; - - // Add cm to multiplied fixed base to get nf - // cm + [poseidon_output + psi] NullifierK - cm.add(layouter.namespace(|| "nf"), &product) - .map(|res| res.extract_p()) +/// Witnesses split_flag. +pub(in crate::circuit) fn assign_split_flag( + layouter: impl Layouter, + column: Column, + split_flag: Value, +) -> Result, plonk::Error> +where + Assigned: for<'v> From<&'v pasta_curves::Fp>, +{ + assign_free_advice( + layouter, + column, + split_flag.map(|split_flag| { + if split_flag { + pallas::Base::one() + } else { + pallas::Base::zero() + } + }), + ) } - -#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] -pub(in crate::circuit) use crate::circuit::commit_ivk::gadgets::commit_ivk; -#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] -pub(in crate::circuit) use crate::circuit::note_commit::gadgets::note_commit; diff --git a/src/circuit/gadget/add_chip.rs b/src/circuit/gadget/add_chip.rs index 3d387f526..3d86bd974 100644 --- a/src/circuit/gadget/add_chip.rs +++ b/src/circuit/gadget/add_chip.rs @@ -1,3 +1,5 @@ +//! `Add` chip implementation. + use halo2_proofs::{ circuit::{AssignedCell, Chip, Layouter}, plonk::{self, Advice, Column, ConstraintSystem, Constraints, Selector}, diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index eed0c180c..5b956528c 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -10,7 +10,7 @@ use core::iter; use group::ff::PrimeField; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Value}, + circuit::{AssignedCell, Chip, Layouter, Value}, plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, poly::Rotation, }; @@ -23,22 +23,21 @@ use crate::{ use halo2_gadgets::{ ecc::{ chip::{EccChip, NonIdentityEccPoint}, - Point, ScalarFixed, + NonIdentityPoint, Point, ScalarFixed, }, sinsemilla::{ chip::{SinsemillaChip, SinsemillaConfig}, CommitDomain, Message, MessagePiece, }, utilities::{ - bool_check, - lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, + bool_check, cond_swap::CondSwapChip, lookup_range_check::PallasLookupRangeCheck, FieldValue, RangeConstrained, }, }; -type NoteCommitPiece = MessagePiece< +type NoteCommitPiece = MessagePiece< pallas::Affine, - SinsemillaChip, + SinsemillaChip, 10, 253, >; @@ -73,14 +72,15 @@ type CanonicityBounds = ( /// /// #[derive(Clone, Debug)] -struct DecomposeB { +struct DecomposeB { q_notecommit_b: Selector, col_l: Column, col_m: Column, col_r: Column, + _lookup_marker: core::marker::PhantomData, } -impl DecomposeB { +impl DecomposeB { fn configure( meta: &mut ConstraintSystem, col_l: Column, @@ -125,19 +125,20 @@ impl DecomposeB { col_l, col_m, col_r, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::type_complexity)] fn decompose( - lookup_config: &LookupRangeCheckConfig, - chip: SinsemillaChip, + lookup_config: &Lookup, + chip: SinsemillaChip, layouter: &mut impl Layouter, g_d: &NonIdentityEccPoint, pk_d: &NonIdentityEccPoint, ) -> Result< ( - NoteCommitPiece, + NoteCommitPiece, RangeConstrained>, RangeConstrained>, RangeConstrained>, @@ -179,7 +180,7 @@ impl DecomposeB { fn assign( &self, layouter: &mut impl Layouter, - b: NoteCommitPiece, + b: NoteCommitPiece, b_0: RangeConstrained>, b_1: RangeConstrained>, b_2: RangeConstrained>, @@ -218,14 +219,15 @@ impl DecomposeB { /// /// #[derive(Clone, Debug)] -struct DecomposeD { +struct DecomposeD { q_notecommit_d: Selector, col_l: Column, col_m: Column, col_r: Column, + _lookup_marker: core::marker::PhantomData, } -impl DecomposeD { +impl DecomposeD { fn configure( meta: &mut ConstraintSystem, col_l: Column, @@ -270,19 +272,20 @@ impl DecomposeD { col_l, col_m, col_r, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::type_complexity)] fn decompose( - lookup_config: &LookupRangeCheckConfig, - chip: SinsemillaChip, + lookup_config: &Lookup, + chip: SinsemillaChip, layouter: &mut impl Layouter, pk_d: &NonIdentityEccPoint, value: &AssignedCell, ) -> Result< ( - NoteCommitPiece, + NoteCommitPiece, RangeConstrained>, RangeConstrained>, RangeConstrained>, @@ -318,7 +321,7 @@ impl DecomposeD { fn assign( &self, layouter: &mut impl Layouter, - d: NoteCommitPiece, + d: NoteCommitPiece, d_0: RangeConstrained>, d_1: RangeConstrained>, d_2: RangeConstrained>, @@ -354,14 +357,15 @@ impl DecomposeD { /// /// #[derive(Clone, Debug)] -struct DecomposeE { +struct DecomposeE { q_notecommit_e: Selector, col_l: Column, col_m: Column, col_r: Column, + _lookup_marker: core::marker::PhantomData, } -impl DecomposeE { +impl DecomposeE { fn configure( meta: &mut ConstraintSystem, col_l: Column, @@ -392,19 +396,20 @@ impl DecomposeE { col_l, col_m, col_r, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::type_complexity)] fn decompose( - lookup_config: &LookupRangeCheckConfig, - chip: SinsemillaChip, + lookup_config: &Lookup, + chip: SinsemillaChip, layouter: &mut impl Layouter, value: &AssignedCell, rho: &AssignedCell, ) -> Result< ( - NoteCommitPiece, + NoteCommitPiece, RangeConstrained>, RangeConstrained>, ), @@ -440,7 +445,7 @@ impl DecomposeE { fn assign( &self, layouter: &mut impl Layouter, - e: NoteCommitPiece, + e: NoteCommitPiece, e_0: RangeConstrained>, e_1: RangeConstrained>, ) -> Result<(), Error> { @@ -473,13 +478,14 @@ impl DecomposeE { /// /// #[derive(Clone, Debug)] -struct DecomposeG { +struct DecomposeG { q_notecommit_g: Selector, col_l: Column, col_m: Column, + _lookup_marker: core::marker::PhantomData, } -impl DecomposeG { +impl DecomposeG { fn configure( meta: &mut ConstraintSystem, col_l: Column, @@ -517,19 +523,20 @@ impl DecomposeG { q_notecommit_g, col_l, col_m, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::type_complexity)] fn decompose( - lookup_config: &LookupRangeCheckConfig, - chip: SinsemillaChip, + lookup_config: &Lookup, + chip: SinsemillaChip, layouter: &mut impl Layouter, rho: &AssignedCell, psi: &AssignedCell, ) -> Result< ( - NoteCommitPiece, + NoteCommitPiece, RangeConstrained>, RangeConstrained>, ), @@ -561,7 +568,7 @@ impl DecomposeG { fn assign( &self, layouter: &mut impl Layouter, - g: NoteCommitPiece, + g: NoteCommitPiece, g_0: RangeConstrained>, g_1: RangeConstrained>, z1_g: AssignedCell, @@ -586,6 +593,7 @@ impl DecomposeG { } } +/// For Vanilla Orchard circuit, /// h = h_0 || h_1 || h_2 /// = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits /// @@ -595,14 +603,15 @@ impl DecomposeG { /// /// #[derive(Clone, Debug)] -struct DecomposeH { +struct DecomposeHVanilla { q_notecommit_h: Selector, col_l: Column, col_m: Column, col_r: Column, + _lookup_marker: core::marker::PhantomData, } -impl DecomposeH { +impl DecomposeHVanilla { fn configure( meta: &mut ConstraintSystem, col_l: Column, @@ -639,18 +648,19 @@ impl DecomposeH { col_l, col_m, col_r, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::type_complexity)] fn decompose( - lookup_config: &LookupRangeCheckConfig, - chip: SinsemillaChip, + lookup_config: &Lookup, + chip: SinsemillaChip, layouter: &mut impl Layouter, psi: &AssignedCell, ) -> Result< ( - NoteCommitPiece, + NoteCommitPiece, RangeConstrained>, RangeConstrained>, ), @@ -683,7 +693,7 @@ impl DecomposeH { fn assign( &self, layouter: &mut impl Layouter, - h: NoteCommitPiece, + h: NoteCommitPiece, h_0: RangeConstrained>, h_1: RangeConstrained>, ) -> Result, Error> { @@ -705,6 +715,282 @@ impl DecomposeH { } } +/// For ZSA Orchard circuit, +/// h_zec = h_0 || h_1 || h_2_zec +/// = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits +/// +/// h_zsa = h_0 || h_1 || h_2_zsa +/// = (bits 249..=253 of psi) || (bit 254 of psi) || (bits 0..=3 of x(asset)) +/// +/// | A_6 | A_7 | A_8 | q_notecommit_h | +/// -------------------------------------------- +/// | h_zec | h_0 | h_1 | 1 | +/// | h_zsa | h_2_zsa | | 0 | +#[derive(Clone, Debug)] +struct DecomposeHZsa { + q_notecommit_h: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + _lookup_marker: core::marker::PhantomData, +} + +impl DecomposeHZsa { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two_pow_5: pallas::Base, + two_pow_6: pallas::Base, + ) -> Self { + let q_notecommit_h = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece h", |meta| { + let q_notecommit_h = meta.query_selector(q_notecommit_h); + + // h_zec has been constrained to 10 bits by the Sinsemilla hash. + let h_zec = meta.query_advice(col_l, Rotation::cur()); + // h_0 has been constrained to be 5 bits outside this gate. + let h_0 = meta.query_advice(col_m, Rotation::cur()); + // This gate constrains h_1 to be boolean. + let h_1 = meta.query_advice(col_r, Rotation::cur()); + + // h_zsa has been constrained to 10 bits by the Sinsemilla hash. + let h_zsa = meta.query_advice(col_l, Rotation::next()); + // h_2_zsa has been constrained to be 4 bits outside this gate. + let h_2_zsa = meta.query_advice(col_m, Rotation::next()); + + // h_zec = h_0 + (2^5) h_1 + let zec_decomposition_check = h_zec - (h_0.clone() + h_1.clone() * two_pow_5); + + // h_zsa = h_0 + (2^5) h_1 + (2^6) h_2_zsa + let zsa_decomposition_check = + h_zsa - (h_0 + h_1.clone() * two_pow_5 + h_2_zsa * two_pow_6); + + Constraints::with_selector( + q_notecommit_h, + [ + ("bool_check h_1", bool_check(h_1)), + ("zec_decomposition", zec_decomposition_check), + ("zsa_decomposition", zsa_decomposition_check), + ], + ) + }); + + Self { + q_notecommit_h, + col_l, + col_m, + col_r, + _lookup_marker: core::marker::PhantomData, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &Lookup, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + psi: &AssignedCell, + asset: &NonIdentityEccPoint, + ) -> Result< + ( + NoteCommitPiece, + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + // Constrain h_0 to be 5 bits. + let h_0 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "h_0"), + psi.value(), + 249..254, + )?; + + // h_1 will be boolean-constrained in the gate. + let h_1 = RangeConstrained::bitrange_of(psi.value(), 254..255); + + // Constrain h_2_zsa to be 4 bits. + let h_2_zsa = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "h_2_zsa"), + asset.x().value(), + 0..4, + )?; + + let h_zec = MessagePiece::from_subpieces( + chip.clone(), + layouter.namespace(|| "h_zec"), + [ + h_0.value(), + h_1, + RangeConstrained::bitrange_of(Value::known(&pallas::Base::zero()), 0..4), + ], + )?; + + let h_zsa = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "h_zsa"), + [h_0.value(), h_1, h_2_zsa.value()], + )?; + + Ok((h_zec, h_zsa, h_0, h_1, h_2_zsa)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + h_zec: NoteCommitPiece, + h_zsa: NoteCommitPiece, + h_0: RangeConstrained>, + h_1: RangeConstrained>, + h_2_zsa: RangeConstrained>, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece h", + |mut region| { + self.q_notecommit_h.enable(&mut region, 0)?; + + h_zec + .inner() + .cell_value() + .copy_advice(|| "h_zec", &mut region, self.col_l, 0)?; + h_0.inner() + .copy_advice(|| "h_0", &mut region, self.col_m, 0)?; + let h_1 = region.assign_advice(|| "h_1", self.col_r, 0, || *h_1.inner())?; + + h_zsa + .inner() + .cell_value() + .copy_advice(|| "h_zsa", &mut region, self.col_l, 1)?; + + h_2_zsa + .inner() + .copy_advice(|| "h_2_zsa", &mut region, self.col_m, 1)?; + + Ok(h_1) + }, + ) + } +} + +/// j = j_0 || j_1 +/// = (bit 254 of x(asset)) || (ỹ bit of asset) +/// +/// | A_6 | A_7 | A_8 | q_notecommit_j | +/// ------------------------------------ +/// | j | j_0 | j_1 | 1 | +#[derive(Clone, Debug)] +struct DecomposeJ { + q_notecommit_j: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + _lookup_marker: core::marker::PhantomData, +} + +impl DecomposeJ { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two: pallas::Base, + ) -> Self { + let q_notecommit_j = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece j", |meta| { + let q_notecommit_j = meta.query_selector(q_notecommit_j); + + // j has been constrained to 10 bits by the Sinsemilla hash. + let j = meta.query_advice(col_l, Rotation::cur()); + // This gate constrains j_0 to be boolean. + let j_0 = meta.query_advice(col_m, Rotation::cur()); + // This gate constrains j_1 to be boolean. + let j_1 = meta.query_advice(col_r, Rotation::cur()); + + // j = j_0 + (2) j_1 + let decomposition_check = j - (j_0.clone() + j_1.clone() * two); + + Constraints::with_selector( + q_notecommit_j, + [ + ("bool_check j_0", bool_check(j_0)), + ("bool_check j_1", bool_check(j_1)), + ("decomposition", decomposition_check), + ], + ) + }); + + Self { + q_notecommit_j, + col_l, + col_m, + col_r, + _lookup_marker: core::marker::PhantomData, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + chip: SinsemillaChip, + layouter: &mut impl Layouter, + asset: &NonIdentityEccPoint, + ) -> Result< + ( + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + // j_0, j_1 will be boolean-constrained in the gate. + let j_0 = RangeConstrained::bitrange_of(asset.x().value(), 254..255); + let j_1 = RangeConstrained::bitrange_of(asset.y().value(), 0..1); + + let j = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "j"), + [ + j_0, + j_1, + RangeConstrained::bitrange_of(Value::known(&pallas::Base::zero()), 0..8), + ], + )?; + + Ok((j, j_0, j_1)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + j: NoteCommitPiece, + j_0: RangeConstrained>, + j_1: RangeConstrained>, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece j", + |mut region| { + self.q_notecommit_j.enable(&mut region, 0)?; + + j.inner() + .cell_value() + .copy_advice(|| "j", &mut region, self.col_l, 0)?; + let j_0 = region.assign_advice(|| "j_0", self.col_m, 0, || *j_0.inner())?; + j_1.inner() + .copy_advice(|| "j_1", &mut region, self.col_r, 0)?; + + Ok(j_0) + }, + ) + } +} + /// | A_6 | A_7 | A_8 | A_9 | q_notecommit_g_d | /// ----------------------------------------------------------- /// | x(g_d) | b_0 | a | z13_a | 1 | @@ -712,15 +998,16 @@ impl DecomposeH { /// /// #[derive(Clone, Debug)] -struct GdCanonicity { +struct GdCanonicity { q_notecommit_g_d: Selector, col_l: Column, col_m: Column, col_r: Column, col_z: Column, + _lookup_marker: core::marker::PhantomData, } -impl GdCanonicity { +impl GdCanonicity { #[allow(clippy::too_many_arguments)] fn configure( meta: &mut ConstraintSystem, @@ -784,6 +1071,7 @@ impl GdCanonicity { col_m, col_r, col_z, + _lookup_marker: core::marker::PhantomData, } } @@ -792,7 +1080,7 @@ impl GdCanonicity { &self, layouter: &mut impl Layouter, g_d: &NonIdentityEccPoint, - a: NoteCommitPiece, + a: NoteCommitPiece, b_0: RangeConstrained>, b_1: AssignedCell, a_prime: AssignedCell, @@ -822,22 +1110,30 @@ impl GdCanonicity { } } -/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_pk_d | -/// ------------------------------------------------------------------- -/// | x(pk_d) | b_3 | c | z13_c | 1 | -/// | | d_0 | b3_c_prime | z14_b3_c_prime | 0 | +/// For pk_d +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_pk_d_asset | +/// ------------------------------------------------------------------------- +/// | x(pk_d) | b_3 | c | z13_c | 1 | +/// | | d_0 | b3_c_prime | z14_b3_c_prime | 0 | +/// +/// For asset +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_pk_d_asset | +/// ------------------------------------------------------------------------------ +/// | x(asset) | h_2_zsa | i | z13_i | 1 | +/// | | j_0 | h2_i_prime | z14_h2_i_prime | 0 | /// /// #[derive(Clone, Debug)] -struct PkdCanonicity { - q_notecommit_pk_d: Selector, +struct PkdAssetCanonicity { + q_notecommit_pk_d_asset: Selector, col_l: Column, col_m: Column, col_r: Column, col_z: Column, + _lookup_marker: core::marker::PhantomData, } -impl PkdCanonicity { +impl PkdAssetCanonicity { #[allow(clippy::too_many_arguments)] fn configure( meta: &mut ConstraintSystem, @@ -850,10 +1146,13 @@ impl PkdCanonicity { two_pow_254: pallas::Base, t_p: Expression, ) -> Self { - let q_notecommit_pk_d = meta.selector(); + let q_notecommit_pk_d_asset = meta.selector(); - meta.create_gate("NoteCommit input pk_d", |meta| { - let q_notecommit_pk_d = meta.query_selector(q_notecommit_pk_d); + meta.create_gate("NoteCommit input pk_d or asset", |meta| { + // The comments and variable names are for `pk_d` + // This gate is also used with `asset`. + // We have just to replace `pk_d`, `b_3`, `c`, `d_0` by `asset`, `h_2_zsa`, `i`, `j_0` + let q_notecommit_pk_d_asset = meta.query_selector(q_notecommit_pk_d_asset); let pkd_x = meta.query_advice(col_l, Rotation::cur()); @@ -886,7 +1185,7 @@ impl PkdCanonicity { .map(move |(name, poly)| (name, d_0.clone() * poly)); Constraints::with_selector( - q_notecommit_pk_d, + q_notecommit_pk_d_asset, iter::empty() .chain(Some(("decomposition", decomposition_check))) .chain(Some(("b3_c_prime_check", b3_c_prime_check))) @@ -895,28 +1194,32 @@ impl PkdCanonicity { }); Self { - q_notecommit_pk_d, + q_notecommit_pk_d_asset, col_l, col_m, col_r, col_z, + _lookup_marker: core::marker::PhantomData, } } #[allow(clippy::too_many_arguments)] fn assign( + // This function is used for `pk_d` and `asset`. + // For `pk_d`, inputs are `pk_d`, `b_3`, `c`, `d_0`, `b3_c_prime`, `z13_c`, `z14_b3_c_prime` + // For `asset`, inputs are `asset`, `h_2_zsa`, `i`, `j_0`, `h2_i_prime`, `z13_i`, `z14_h2_i_prime` &self, layouter: &mut impl Layouter, pk_d: &NonIdentityEccPoint, b_3: RangeConstrained>, - c: NoteCommitPiece, + c: NoteCommitPiece, d_0: AssignedCell, b3_c_prime: AssignedCell, z13_c: AssignedCell, z14_b3_c_prime: AssignedCell, ) -> Result<(), Error> { layouter.assign_region( - || "NoteCommit input pk_d", + || "NoteCommit input pk_d or asset", |mut region| { pk_d.x() .copy_advice(|| "pkd_x", &mut region, self.col_l, 0)?; @@ -933,7 +1236,7 @@ impl PkdCanonicity { z13_c.copy_advice(|| "z13_c", &mut region, self.col_z, 0)?; z14_b3_c_prime.copy_advice(|| "z14_b3_c_prime", &mut region, self.col_z, 1)?; - self.q_notecommit_pk_d.enable(&mut region, 0) + self.q_notecommit_pk_d_asset.enable(&mut region, 0) }, ) } @@ -1023,15 +1326,16 @@ impl ValueCanonicity { /// /// #[derive(Clone, Debug)] -struct RhoCanonicity { +struct RhoCanonicity { q_notecommit_rho: Selector, col_l: Column, col_m: Column, col_r: Column, col_z: Column, + _lookup_marker: core::marker::PhantomData, } -impl RhoCanonicity { +impl RhoCanonicity { #[allow(clippy::too_many_arguments)] fn configure( meta: &mut ConstraintSystem, @@ -1093,6 +1397,7 @@ impl RhoCanonicity { col_m, col_r, col_z, + _lookup_marker: core::marker::PhantomData, } } @@ -1102,7 +1407,7 @@ impl RhoCanonicity { layouter: &mut impl Layouter, rho: AssignedCell, e_1: RangeConstrained>, - f: NoteCommitPiece, + f: NoteCommitPiece, g_0: AssignedCell, e1_f_prime: AssignedCell, z13_f: AssignedCell, @@ -1425,30 +1730,47 @@ impl YCanonicity { /// Sinsemilla configuration. #[allow(non_snake_case)] #[derive(Clone, Debug)] -pub struct NoteCommitConfig { - b: DecomposeB, - d: DecomposeD, - e: DecomposeE, - g: DecomposeG, - h: DecomposeH, - g_d: GdCanonicity, - pk_d: PkdCanonicity, +pub struct NoteCommitConfig { + b: DecomposeB, + d: DecomposeD, + e: DecomposeE, + g: DecomposeG, + g_d: GdCanonicity, + pk_d_asset: PkdAssetCanonicity, value: ValueCanonicity, - rho: RhoCanonicity, + rho: RhoCanonicity, psi: PsiCanonicity, y_canon: YCanonicity, advices: [Column; 10], sinsemilla_config: - SinsemillaConfig, + SinsemillaConfig, + specific_config_for_circuit: SpecificConfigForCircuit, +} + +#[derive(Clone, Debug)] +pub(crate) enum SpecificConfigForCircuit { + Vanilla(NoteCommitConfigForVanillaCircuit), + Zsa(NoteCommitConfigForZsaCircuit), +} + +#[derive(Clone, Debug)] +pub(crate) struct NoteCommitConfigForVanillaCircuit { + h_vanilla: DecomposeHVanilla, +} + +#[derive(Clone, Debug)] +pub(crate) struct NoteCommitConfigForZsaCircuit { + h_zsa: DecomposeHZsa, + j: DecomposeJ, } /// A Halo 2 chip that proves correct evaluation of the `NoteCommit` gadget. #[derive(Clone, Debug)] -pub struct NoteCommitChip { - config: NoteCommitConfig, +pub struct NoteCommitChip { + config: NoteCommitConfig, } -impl NoteCommitChip { +impl NoteCommitChip { /// Configures the chip's gates, Sinsemilla instances, and canonicity checks. #[allow(non_snake_case)] #[allow(clippy::many_single_char_names)] @@ -1460,8 +1782,10 @@ impl NoteCommitChip { OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases, + Lookup, >, - ) -> NoteCommitConfig { + is_zsa_circuit: bool, + ) -> NoteCommitConfig { // Useful constants let two = pallas::Base::from(2); let two_pow_2 = pallas::Base::from(1 << 2); @@ -1490,7 +1814,14 @@ impl NoteCommitChip { let d = DecomposeD::configure(meta, col_l, col_m, col_r, two, two_pow_2, two_pow_10); let e = DecomposeE::configure(meta, col_l, col_m, col_r, two_pow_6); let g = DecomposeG::configure(meta, col_l, col_m, two, two_pow_10); - let h = DecomposeH::configure(meta, col_l, col_m, col_r, two_pow_5); + let specific_config_for_circuit = if is_zsa_circuit { + let h = DecomposeHZsa::configure(meta, col_l, col_m, col_r, two_pow_5, two_pow_6); + let j = DecomposeJ::configure(meta, col_l, col_m, col_r, two); + SpecificConfigForCircuit::Zsa(NoteCommitConfigForZsaCircuit { h_zsa: h, j }) + } else { + let h = DecomposeHVanilla::configure(meta, col_l, col_m, col_r, two_pow_5); + SpecificConfigForCircuit::Vanilla(NoteCommitConfigForVanillaCircuit { h_vanilla: h }) + }; let g_d = GdCanonicity::configure( meta, @@ -1504,7 +1835,7 @@ impl NoteCommitChip { t_p.clone(), ); - let pk_d = PkdCanonicity::configure( + let pk_d_asset = PkdAssetCanonicity::configure( meta, col_l, col_m, @@ -1560,30 +1891,46 @@ impl NoteCommitChip { d, e, g, - h, g_d, - pk_d, + pk_d_asset, value, rho, psi, y_canon, advices, sinsemilla_config, + specific_config_for_circuit, } } /// Constructs the chip from a [`NoteCommitConfig`]. #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] - pub(in crate::circuit) fn construct(config: NoteCommitConfig) -> Self { + pub(in crate::circuit) fn construct(config: NoteCommitConfig) -> Self { Self { config } } } +/// ZSA-specific inputs required by the `NoteCommit` gadget. +#[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] +#[derive(Debug)] +pub(crate) struct ZsaNoteCommitParams { + pub(crate) cond_swap_chip: CondSwapChip, + pub(crate) asset: NonIdentityEccPoint, + pub(crate) is_zatoshi_asset: AssignedCell, +} + +pub(crate) struct ZsaFinalDecomposition { + pub(crate) h_zsa: NoteCommitPiece, + pub(crate) h_2_zsa: RangeConstrained>, + pub(crate) i: NoteCommitPiece, + pub(crate) j: NoteCommitPiece, + pub(crate) j_0: RangeConstrained>, + pub(crate) j_1: RangeConstrained>, +} + /// Gadget functions for `NoteCommit` operations. #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] pub(in crate::circuit) mod gadgets { - use halo2_proofs::circuit::{Chip, Value}; - use super::*; /// Computes the note commitment in-circuit. @@ -1591,18 +1938,25 @@ pub(in crate::circuit) mod gadgets { #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] #[cfg_attr(feature = "unstable-voting-circuits", visibility::make(pub))] - pub(in crate::circuit) fn note_commit( + pub(in crate::circuit) fn note_commit( mut layouter: impl Layouter, - chip: SinsemillaChip, - ecc_chip: EccChip, - note_commit_chip: NoteCommitChip, + chip: SinsemillaChip, + ecc_chip: EccChip, + note_commit_chip: NoteCommitChip, g_d: &NonIdentityEccPoint, pk_d: &NonIdentityEccPoint, value: AssignedCell, rho: AssignedCell, psi: AssignedCell, - rcm: ScalarFixed>, - ) -> Result>, Error> { + rcm: ScalarFixed>, + zsa_params: Option, + ) -> Result>, Error> { + match (&zsa_params, ¬e_commit_chip.config.specific_config_for_circuit) { + (Some(_), SpecificConfigForCircuit::Zsa(_)) => {}, + (None, SpecificConfigForCircuit::Vanilla(_)) => {}, + _ => panic!("ZSA parameters must be provided for ZSA circuits, and must not be provided for vanilla circuits"), + } + let lookup_config = chip.config().lookup_config(); // `a` = bits 0..=249 of `x(g_d)` @@ -1645,10 +1999,69 @@ pub(in crate::circuit) mod gadgets { let (g, g_0, g_1) = DecomposeG::decompose(&lookup_config, chip.clone(), &mut layouter, &rho, &psi)?; - // h = h_0 || h_1 || h_2 - // = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits - let (h, h_0, h_1) = - DecomposeH::decompose(&lookup_config, chip.clone(), &mut layouter, &psi)?; + let (h_zec, h_0, h_1, zsa_decomposition) = match &zsa_params { + None => { + // h_zec = h_0 || h_1 || h_2 + // = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits + let (h_zec, h_0, h_1) = DecomposeHVanilla::decompose( + &lookup_config, + chip.clone(), + &mut layouter, + &psi, + )?; + (h_zec, h_0, h_1, None) + } + Some(zsa_params) => { + // h_zec = h_0 || h_1 || h_2_zec + // = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits + // h_zsa = h_0 || h_1 || h_2_zsa + // = (bits 249..=253 of psi) || (bit 254 of psi) || (bits 0..=3 of x(asset)) + let (h_zec, h_zsa, h_0, h_1, h_2_zsa) = DecomposeHZsa::decompose( + &lookup_config, + chip.clone(), + &mut layouter, + &psi, + &zsa_params.asset, + )?; + + // i = bits 4..=253 of asset + let i = MessagePiece::from_subpieces( + chip.clone(), + layouter.namespace(|| "i"), + [RangeConstrained::bitrange_of( + zsa_params.asset.x().value(), + 4..254, + )], + )?; + + // j = j_0 || j_1 || j_2 = (bit 254 of x(asset)) || (ỹ bit of asset) || 8 zero bits + let (j, j_0, j_1) = + DecomposeJ::decompose(chip.clone(), &mut layouter, &zsa_params.asset)?; + + // Check decomposition of `y(asset)`. + let j_1 = y_canonicity( + &lookup_config, + ¬e_commit_chip.config.y_canon, + layouter.namespace(|| "y(asset) decomposition"), + zsa_params.asset.y(), + j_1, + )?; + + ( + h_zec, + h_0, + h_1, + Some(ZsaFinalDecomposition { + h_zsa, + h_2_zsa, + i, + j, + j_0, + j_1, + }), + ) + } + }; // Check decomposition of `y(g_d)`. let b_2 = y_canonicity( @@ -1667,45 +2080,172 @@ pub(in crate::circuit) mod gadgets { d_1, )?; - // cm = NoteCommit^Orchard_rcm(g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi) - // - // `cm = ⊥` is handled internally to `CommitDomain::commit`: incomplete addition - // constraints allows ⊥ to occur, and then during synthesis it detects these edge - // cases and raises an error (aborting proof creation). - // - // https://p.z.cash/ZKS:action-cm-old-integrity?partial - // https://p.z.cash/ZKS:action-cmx-new-integrity?partial - let (cm, zs) = { - let message = Message::from_pieces( - chip.clone(), - vec![ - a.clone(), - b.clone(), - c.clone(), - d.clone(), - e.clone(), - f.clone(), - g.clone(), - h.clone(), - ], - ); - let domain = CommitDomain::new(chip, ecc_chip, &OrchardCommitDomains::NoteCommit); - domain.commit( - layouter.namespace(|| "Process NoteCommit inputs"), - message, - rcm, - )? + let (cm, zs_common, zs_zsa_suffix) = match (&zsa_params, &zsa_decomposition) { + (None, None) => { + // cm = NoteCommit^Orchard_rcm(g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi) + // + // `cm = ⊥` is handled internally to `CommitDomain::commit`: incomplete addition + // constraints allows ⊥ to occur, and then during synthesis it detects these edge + // cases and raises an error (aborting proof creation). + // + // https://p.z.cash/ZKS:action-cm-old-integrity?partial + // https://p.z.cash/ZKS:action-cmx-new-integrity?partial + let message = Message::from_pieces( + chip.clone(), + vec![ + a.clone(), + b.clone(), + c.clone(), + d.clone(), + e.clone(), + f.clone(), + g.clone(), + h_zec.clone(), + ], + ); + let domain = CommitDomain::new(chip, ecc_chip, &OrchardCommitDomains::NoteCommit); + let (cm, zs) = domain.commit( + layouter.namespace(|| "Process NoteCommit inputs"), + message, + rcm, + )?; + (cm, zs, vec![]) + } + (Some(zsa_params), Some(zsa_decomposition)) => { + // cm = NoteCommit^Orchard_rcm(g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi || asset) + // + // `cm = ⊥` is handled internally to `CommitDomain::commit`: incomplete addition + // constraints allows ⊥ to occur, and then during synthesis it detects these edge + // cases and raises an error (aborting proof creation). + // + // See [ZIP-226: Transfer and Burn of Zcash Shielded Assets][zip226] for more details. + // [zip226]: https://zips.z.cash/zip-0226#note-structure-commitment. + let message_common_prefix = Message::from_pieces( + chip.clone(), + vec![ + a.clone(), + b.clone(), + c.clone(), + d.clone(), + e.clone(), + f.clone(), + g.clone(), + ], + ); + + let message_suffix_zec = Message::from_pieces(chip.clone(), vec![h_zec.clone()]); + + let message_suffix_zsa = Message::from_pieces( + chip.clone(), + vec![ + zsa_decomposition.h_zsa.clone(), + zsa_decomposition.i.clone(), + zsa_decomposition.j.clone(), + ], + ); + + // We will evaluate + // - `hash_point_zec = hash(Q_ZEC, message_common_prefix || message_suffix_zec)`, and + // - `hash_point_zsa = hash(Q_ZSA, message_common_prefix || message_suffix_zsa)`. + // by sharing a portion of the hash evaluation process between `hash_point_zec` and + // `hash_point_zsa`: + // 1. Q = if (is_zatoshi_asset == 0) {Q_ZSA} else {Q_ZEC} + // 2. common_hash = hash(Q, message_common_prefix) // this part is shared + // 3. hash_point_zec = hash(common_hash, message_suffix_zec) + // 4. hash_point_zsa = hash(common_hash, message_suffix_zsa) + // 5. hash_point = if (is_zatoshi_asset == 0) {hash_point_zsa} else {hash_point_zec} + let zec_domain = CommitDomain::new( + chip.clone(), + ecc_chip.clone(), + &OrchardCommitDomains::NoteCommit, + ); + let zsa_domain = + CommitDomain::new(chip, ecc_chip.clone(), &OrchardCommitDomains::NoteZsaCommit); + + // Perform a MUX to select the desired initial Q point + // q_init = q_init_zec if is_zatoshi_asset is true + // q_init = q_init_zsa if is_zatoshi_asset is false + let q_init = { + let q_init_zec = NonIdentityPoint::new_from_constant( + ecc_chip.clone(), + layouter.namespace(|| "q_init_zec"), + zec_domain.q_init(), + )?; + + let q_init_zsa = NonIdentityPoint::new_from_constant( + ecc_chip.clone(), + layouter.namespace(|| "q_init_zsa"), + zsa_domain.q_init(), + )?; + + zsa_params.cond_swap_chip.mux_on_non_identity_points( + layouter.namespace(|| "mux on hash point"), + &zsa_params.is_zatoshi_asset, + q_init_zsa.inner(), + q_init_zec.inner(), + )? + }; + + // common_hash = hash(q_init, message_common_prefix) + // + // To evaluate the different hash, we could use either zec_domain or zsa_domain + // because we use a private initial point. + let (common_hash, zs_common) = zec_domain.hash_with_private_init( + layouter.namespace(|| "hash common prefix note"), + &q_init, + message_common_prefix, + )?; + + // hash_point_zec = hash(common_hash, message_suffix_zec) = hash(q_init, message_zec) + let (hash_point_zec, _zs_zec) = zec_domain.hash_with_private_init( + layouter.namespace(|| "hash suffix ZEC note"), + common_hash.inner(), + message_suffix_zec, + )?; + + // hash_point_zsa = hash(common_hash, message_suffix_zsa) = hash(q_init, message_zsa) + let (hash_point_zsa, zs_zsa) = zec_domain.hash_with_private_init( + layouter.namespace(|| "hash suffix ZSA note"), + common_hash.inner(), + message_suffix_zsa, + )?; + + // Perform a MUX to select the desired hash point + // hash_point = hash_zec if is_zatoshi_asset is true + // hash_point = hash_zsa if is_zatoshi_asset is false + let hash_point = Point::from_inner( + ecc_chip, + zsa_params.cond_swap_chip.mux_on_points( + layouter.namespace(|| "mux on hash point"), + &zsa_params.is_zatoshi_asset, + &(hash_point_zsa.inner().clone().into()), + &(hash_point_zec.inner().clone().into()), + )?, + ); + + // To evaluate the blinding factor, we could use either zec_domain or zsa_domain + // because they have both the same `R` constant. + let blinding_factor = + zec_domain.blinding_factor(layouter.namespace(|| "[r] R"), rcm)?; + let commitment = + hash_point.add(layouter.namespace(|| "M + [r] R"), &blinding_factor)?; + + (commitment, zs_common, zs_zsa) + } + _ => { + panic!("Either both zsa_params and zsa_decomposition must be provided, or neither.") + } }; - // `CommitDomain::commit` returns the running sum for each `MessagePiece`. Grab + // `CommitDomain::hash` returns the running sum for each `MessagePiece`. Grab // the outputs that we will need for canonicity checks. - let z13_a = zs[0][13].clone(); - let z13_c = zs[2][13].clone(); - let z1_d = zs[3][1].clone(); - let z13_f = zs[5][13].clone(); - let z1_g = zs[6][1].clone(); + let z13_a = zs_common[0][13].clone(); + let z13_c = zs_common[2][13].clone(); + let z1_d = zs_common[3][1].clone(); + let z13_f = zs_common[5][13].clone(); + let z1_g = zs_common[6][1].clone(); let g_2 = z1_g.clone(); - let z13_g = zs[6][13].clone(); + let z13_g = zs_common[6][13].clone(); // Witness and constrain the bounds we need to ensure canonicity. let (a_prime, z13_a_prime) = canon_bitshift_130( @@ -1714,7 +2254,7 @@ pub(in crate::circuit) mod gadgets { a.inner().cell_value(), )?; - let (b3_c_prime, z14_b3_c_prime) = pkd_x_canonicity( + let (b3_c_prime, z14_b3_c_prime) = pkd_asset_x_canonicity( &lookup_config, layouter.namespace(|| "x(pk_d) canonicity"), b_3.clone(), @@ -1736,7 +2276,7 @@ pub(in crate::circuit) mod gadgets { )?; // Finally, assign values to all of the NoteCommit regions. - let cfg = note_commit_chip.config; + let cfg = ¬e_commit_chip.config; let b_1 = cfg .b @@ -1752,12 +2292,30 @@ pub(in crate::circuit) mod gadgets { .g .assign(&mut layouter, g, g_0, g_1.clone(), z1_g.clone())?; - let h_1 = cfg.h.assign(&mut layouter, h, h_0.clone(), h_1)?; + let h_1 = match ( + &zsa_decomposition, + ¬e_commit_chip.config.specific_config_for_circuit, + ) { + (None, SpecificConfigForCircuit::Vanilla(specific_config)) => specific_config + .h_vanilla + .assign(&mut layouter, h_zec, h_0.clone(), h_1)?, + (Some(zsa_decomposition), SpecificConfigForCircuit::Zsa(specific_config)) => { + specific_config.h_zsa.assign( + &mut layouter, + h_zec, + zsa_decomposition.h_zsa.clone(), + h_0.clone(), + h_1, + zsa_decomposition.h_2_zsa.clone(), + )? + } + _ => panic!("zsa_decomposition is required with a ZSA configuration but must be omitted with a Vanilla configuration."), + }; cfg.g_d .assign(&mut layouter, g_d, a, b_0, b_1, a_prime, z13_a, z13_a_prime)?; - cfg.pk_d.assign( + cfg.pk_d_asset.assign( &mut layouter, pk_d, b_3, @@ -1793,6 +2351,42 @@ pub(in crate::circuit) mod gadgets { z13_g1_g2_prime, )?; + if let ( + Some(zsa_decomposition), + Some(zsa_params), + SpecificConfigForCircuit::Zsa(specific_config), + ) = ( + zsa_decomposition, + &zsa_params, + ¬e_commit_chip.config.specific_config_for_circuit, + ) { + let j_0 = specific_config.j.assign( + &mut layouter, + zsa_decomposition.j, + zsa_decomposition.j_0, + zsa_decomposition.j_1, + )?; + + let (h2_i_prime, z14_h2_i_prime) = pkd_asset_x_canonicity( + &lookup_config, + layouter.namespace(|| "x(asset) canonicity"), + zsa_decomposition.h_2_zsa.clone(), + zsa_decomposition.i.inner().cell_value(), + )?; + + let z13_i = zs_zsa_suffix[1][13].clone(); + cfg.pk_d_asset.assign( + &mut layouter, + &zsa_params.asset, + zsa_decomposition.h_2_zsa, + zsa_decomposition.i, + j_0, + h2_i_prime, + z13_i, + z14_h2_i_prime, + )?; + } + Ok(cm) } @@ -1801,8 +2395,8 @@ pub(in crate::circuit) mod gadgets { /// Specifications: /// - [`g_d` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-g_d?partial) /// - [`y` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial) - fn canon_bitshift_130( - lookup_config: &LookupRangeCheckConfig, + fn canon_bitshift_130( + lookup_config: &Lookup, mut layouter: impl Layouter, a: AssignedCell, ) -> Result { @@ -1832,15 +2426,16 @@ pub(in crate::circuit) mod gadgets { Ok((a_prime, zs[13].clone())) } - /// Check canonicity of `x(pk_d)` encoding. + /// Check canonicity of `x(pk_d)` and `x(asset)` encoding. /// /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-pk_d?partial). - fn pkd_x_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn pkd_asset_x_canonicity( + lookup_config: &Lookup, mut layouter: impl Layouter, b_3: RangeConstrained>, c: AssignedCell, ) -> Result { + // Example for `x(pk_d)`: // `x(pk_d)` = `b_3 (4 bits) || c (250 bits) || d_0 (1 bit)` // - d_0 = 1 => b_3 + 2^4 c < t_P // - 0 ≤ b_3 + 2^4 c < 2^134 @@ -1850,6 +2445,7 @@ pub(in crate::circuit) mod gadgets { // - z_13 of SinsemillaHash(c) == 0 constrains bits 4..=253 of pkd_x // to 130 bits. z13_c is directly checked in the gate. // - 0 ≤ b_3 + 2^4 c + 2^140 - t_P < 2^140 (14 ten-bit lookups) + // For `x(asset)`, we have to replace `pk_d`, `b_3`, `c`, `d_0` by `asset`, `h_2_zsa`, `i`, `j_0` // Decompose the low 140 bits of b3_c_prime = b_3 + 2^4 c + 2^140 - t_P, // and output the running sum at the end of it. @@ -1876,8 +2472,8 @@ pub(in crate::circuit) mod gadgets { /// Check canonicity of `rho` encoding. /// /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-rho?partial). - fn rho_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn rho_canonicity( + lookup_config: &Lookup, mut layouter: impl Layouter, e_1: RangeConstrained>, f: AssignedCell, @@ -1917,8 +2513,8 @@ pub(in crate::circuit) mod gadgets { /// Check canonicity of `psi` encoding. /// /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-psi?partial). - fn psi_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn psi_canonicity( + lookup_config: &Lookup, mut layouter: impl Layouter, g_1: RangeConstrained>, g_2: AssignedCell, @@ -1959,8 +2555,8 @@ pub(in crate::circuit) mod gadgets { /// Specifications: /// - [`y` decomposition](https://p.z.cash/orchard-0.1:note-commit-decomposition-y?partial) /// - [`y` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial) - fn y_canonicity( - lookup_config: &LookupRangeCheckConfig, + fn y_canonicity( + lookup_config: &Lookup, y_canon: &YCanonicity, mut layouter: impl Layouter, y: AssignedCell, @@ -2037,16 +2633,16 @@ pub(in crate::circuit) mod gadgets { mod tests { use core::iter; - use super::NoteCommitConfig; use crate::{ circuit::{ - gadget::assign_free_advice, - note_commit::{gadgets, NoteCommitChip}, + gadget::{assign_free_advice, assign_is_zatoshi_asset}, + note_commit::{gadgets, NoteCommitChip, NoteCommitConfig, ZsaNoteCommitParams}, }, constants::{ fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, L_ORCHARD_BASE, L_VALUE, T_Q, }, + note::{commitment::NoteCommitTrapdoor, AssetBase, NoteCommitment}, value::NoteValue, }; use halo2_gadgets::{ @@ -2056,22 +2652,28 @@ mod tests { }, sinsemilla::chip::SinsemillaChip, sinsemilla::primitives::CommitDomain, - utilities::lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, + utilities::{ + cond_swap::{CondSwapChip, CondSwapConfig}, + lookup_range_check::{ + LookupRangeCheck, LookupRangeCheckConfig, PallasLookupRangeCheck4_5BConfig, + PallasLookupRangeCheckConfig, + }, + }, }; use ff::{Field, PrimeField, PrimeFieldBits}; - use group::Curve; + use group::{Curve, Group, GroupEncoding}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; - use pasta_curves::{arithmetic::CurveAffine, pallas}; + use pasta_curves::{arithmetic::CurveAffine, pallas, EpAffine}; use rand::{rngs::OsRng, RngCore}; #[test] - fn note_commit() { + fn note_commit_vanilla() { #[derive(Default)] struct MyCircuit { gd_x: Value, @@ -2083,7 +2685,10 @@ mod tests { } impl Circuit for MyCircuit { - type Config = (NoteCommitConfig, EccConfig); + type Config = ( + NoteCommitConfig, + EccConfig, + ); type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { @@ -2144,7 +2749,7 @@ mod tests { false, ); let note_commit_config = - NoteCommitChip::configure(meta, advices, sinsemilla_config); + NoteCommitChip::configure(meta, advices, sinsemilla_config, false); let ecc_config = EccChip::::configure( meta, @@ -2262,6 +2867,7 @@ mod tests { rho, psi, rcm_gadget, + None, )?; let expected_cm = { let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); @@ -2380,4 +2986,371 @@ mod tests { assert_eq!(prover.verify(), Ok(())); } } + + #[test] + fn note_commit_zsa() { + #[derive(Default)] + struct MyCircuit { + g_d: Value, + pk_d: Value, + rho: Value, + psi: Value, + asset: Value, + } + + impl Circuit for MyCircuit { + type Config = ( + NoteCommitConfig, + EccConfig, + CondSwapConfig, + ); + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + // Shared fixed column for loading constants. + let constants = meta.fixed_column(); + meta.enable_constant(constants); + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + let table_idx = meta.lookup_table_column(); + let table_range_check_tag = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + ); + let lagrange_coeffs = [ + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ]; + + let range_check = PallasLookupRangeCheck4_5BConfig::configure_with_tag( + meta, + advices[9], + table_idx, + table_range_check_tag, + ); + let sinsemilla_config = SinsemillaChip::< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + PallasLookupRangeCheck4_5BConfig, + >::configure( + meta, + advices[..5].try_into().unwrap(), + advices[2], + lagrange_coeffs[0], + lookup, + range_check, + true, + ); + let note_commit_config = + NoteCommitChip::configure(meta, advices, sinsemilla_config, true); + + let ecc_config = + EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ); + + let cond_swap_config = + CondSwapChip::configure(meta, advices[0..5].try_into().unwrap()); + + (note_commit_config, ecc_config, cond_swap_config) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (note_commit_config, ecc_config, cond_swap_config) = config; + + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + PallasLookupRangeCheck4_5BConfig, + >::load( + note_commit_config.sinsemilla_config.clone(), &mut layouter + )?; + + // Construct a Sinsemilla chip + let sinsemilla_chip = + SinsemillaChip::construct(note_commit_config.sinsemilla_config.clone()); + + // Construct an ECC chip + let ecc_chip = EccChip::construct(ecc_config, CircuitVersion::AnchoredBase); + + // Construct a NoteCommit chip + let note_commit_chip = NoteCommitChip::construct(note_commit_config.clone()); + + // Construct a CondSwap chip + let cond_swap_chip = CondSwapChip::construct(cond_swap_config); + + // Witness g_d + let g_d = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness g_d"), + self.g_d, + )?; + + // Witness pk_d + let pk_d = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d"), + self.pk_d, + )?; + + // Witness a random non-negative u64 note value + // A note value cannot be negative. + let value = { + let mut rng = OsRng; + NoteValue::from_raw(rng.next_u64()) + }; + let value_var = { + assign_free_advice( + layouter.namespace(|| "witness value"), + note_commit_config.advices[0], + Value::known(value), + )? + }; + + // Witness rho + let rho = assign_free_advice( + layouter.namespace(|| "witness rho"), + note_commit_config.advices[0], + self.rho, + )?; + + // Witness psi + let psi = assign_free_advice( + layouter.namespace(|| "witness psi"), + note_commit_config.advices[0], + self.psi, + )?; + + let rcm = pallas::Scalar::random(OsRng); + let rcm_gadget = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcm"), + Value::known(rcm), + )?; + + let asset = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness asset"), + self.asset.map(|asset| asset.cv_base().to_affine()), + )?; + + let is_zatoshi_asset = assign_is_zatoshi_asset( + layouter.namespace(|| "witness is_zatoshi_asset"), + note_commit_config.advices[0], + self.asset, + )?; + let cm = gadgets::note_commit( + layouter.namespace(|| "Hash NoteCommit pieces"), + sinsemilla_chip, + ecc_chip.clone(), + note_commit_chip, + g_d.inner(), + pk_d.inner(), + value_var, + rho, + psi, + rcm_gadget, + Some(ZsaNoteCommitParams { + cond_swap_chip, + asset: asset.inner().clone(), + is_zatoshi_asset, + }), + )?; + let expected_cm = { + // Hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi + let point = self + .g_d + .zip(self.pk_d) + .zip(self.rho.zip(self.psi)) + .zip(self.asset) + .map(|(((g_d, pk_d), (rho, psi)), asset)| { + NoteCommitment::derive( + g_d.to_bytes(), + pk_d.to_bytes(), + value, + asset, + rho, + psi, + NoteCommitTrapdoor::new(rcm), + ) + .unwrap() + .inner() + .to_affine() + }); + NonIdentityPoint::new(ecc_chip, layouter.namespace(|| "witness cm"), point)? + }; + cm.constrain_equal(layouter.namespace(|| "cm == expected cm"), &expected_cm) + } + } + + fn affine_point_from_coordinates(x_coord: pallas::Base, y_lsb: pallas::Base) -> EpAffine { + // Calculate y = (x^3 + 5).sqrt() + let mut y = (x_coord.square() * x_coord + pallas::Affine::b()) + .sqrt() + .unwrap(); + if bool::from(y.is_odd() ^ y_lsb.is_odd()) { + y = -y; + } + pallas::Affine::from_xy(x_coord, y).unwrap() + } + + let two_pow_254 = pallas::Base::from_u128(1 << 127).square(); + let mut rng = OsRng; + let random_asset = AssetBase::random(&mut rng); + + // Test different values of `ak`, `nk` + let mut circuits = vec![]; + for asset in [random_asset, AssetBase::zatoshi()] { + // `gd_x` = -1, `pkd_x` = -1 (these have to be x-coordinates of curve points) + // `rho` = 0, `psi` = 0 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + rho: Value::known(pallas::Base::zero()), + psi: Value::known(pallas::Base::zero()), + asset: Value::known(asset), + }); + // `rho` = T_Q - 1, `psi` = T_Q - 1 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + rho: Value::known(pallas::Base::from_u128(T_Q - 1)), + psi: Value::known(pallas::Base::from_u128(T_Q - 1)), + asset: Value::known(asset), + }); + // `rho` = T_Q, `psi` = T_Q + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + rho: Value::known(pallas::Base::from_u128(T_Q)), + psi: Value::known(pallas::Base::from_u128(T_Q)), + asset: Value::known(asset), + }); + // `rho` = 2^127 - 1, `psi` = 2^127 - 1 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + rho: Value::known(pallas::Base::from_u128((1 << 127) - 1)), + psi: Value::known(pallas::Base::from_u128((1 << 127) - 1)), + asset: Value::known(asset), + }); + // `rho` = 2^127, `psi` = 2^127 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + rho: Value::known(pallas::Base::from_u128(1 << 127)), + psi: Value::known(pallas::Base::from_u128(1 << 127)), + asset: Value::known(asset), + }); + // `rho` = 2^254 - 1, `psi` = 2^254 - 1 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + rho: Value::known(two_pow_254 - pallas::Base::one()), + psi: Value::known(two_pow_254 - pallas::Base::one()), + asset: Value::known(asset), + }); + // `rho` = 2^254, `psi` = 2^254 + circuits.push(MyCircuit { + g_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::one(), + )), + pk_d: Value::known(affine_point_from_coordinates( + -pallas::Base::one(), + pallas::Base::zero(), + )), + rho: Value::known(two_pow_254), + psi: Value::known(two_pow_254), + asset: Value::known(asset), + }); + // Random values + circuits.push(MyCircuit { + g_d: Value::known(pallas::Point::random(rng).to_affine()), + pk_d: Value::known(pallas::Point::random(rng).to_affine()), + rho: Value::known(pallas::Base::random(&mut rng)), + psi: Value::known(pallas::Base::random(&mut rng)), + asset: Value::known(asset), + }); + } + + for circuit in circuits.iter() { + let prover = MockProver::::run(11, circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + } } diff --git a/src/circuit/value_commit_orchard.rs b/src/circuit/value_commit_orchard.rs new file mode 100644 index 000000000..0b03e9abf --- /dev/null +++ b/src/circuit/value_commit_orchard.rs @@ -0,0 +1,391 @@ +//! Value commitment logic for the Orchard circuit. + +use pasta_curves::pallas; + +use crate::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}; + +use halo2_gadgets::{ + ecc::{chip::EccChip, NonIdentityPoint}, + sinsemilla::chip::SinsemillaChip, + utilities::lookup_range_check::PallasLookupRangeCheck, +}; + +pub(in crate::circuit) struct ZsaValueCommitParams { + pub sinsemilla_chip: + SinsemillaChip, + pub asset_base: NonIdentityPoint>, +} + +pub(in crate::circuit) mod gadgets { + use super::*; + + use crate::constants::{OrchardFixedBasesFull, ValueCommitV}; + + use halo2_gadgets::{ + ecc::{FixedPoint, FixedPointShort, Point, ScalarFixed, ScalarFixedShort, ScalarVar}, + sinsemilla, + utilities::lookup_range_check::PallasLookupRangeCheck, + }; + + use halo2_proofs::{ + circuit::{AssignedCell, Chip, Layouter}, + plonk, + }; + + /// `ValueCommit^Orchard` from [ZIP-226: Transfer and Burn of Zcash Shielded Assets][zip226]. + /// + /// [zip226]: https://zips.z.cash/zip-0226#value-commitment-correctness + pub(in crate::circuit) fn value_commit_orchard( + mut layouter: impl Layouter, + ecc_chip: EccChip, + v_net_magnitude_sign: ( + AssignedCell, + AssignedCell, + ), + rcv: ScalarFixed>, + zsa_params: Option>, + ) -> Result>, plonk::Error> { + // Evaluate commitment = [v_net_magnitude_sign] asset + let commitment = match zsa_params { + None => { + let v_net = ScalarFixedShort::new( + ecc_chip.clone(), + layouter.namespace(|| "v_net"), + v_net_magnitude_sign, + )?; + + // commitment = [v_net] ValueCommitV + let (commitment, _) = { + let value_commit_v = + FixedPointShort::from_inner(ecc_chip.clone(), ValueCommitV.into()); + value_commit_v.mul(layouter.namespace(|| "[v] ValueCommitV"), v_net)? + }; + commitment + } + Some(params) => { + // Check that magnitude is 64 bits + // Note: if zsa_params is not provided, this check will be performed inside the + // fixed-base short scalar multiplication. + { + let lookup_config = params.sinsemilla_chip.config().lookup_config(); + let (magnitude_words, magnitude_extra_bits) = (6, 4); + assert_eq!( + magnitude_words * sinsemilla::primitives::K + magnitude_extra_bits, + 64 + ); + let magnitude_zs = lookup_config.copy_check( + layouter.namespace(|| "magnitude lowest 60 bits"), + v_net_magnitude_sign.0.clone(), + magnitude_words, // 6 windows of 10 bits. + false, // Do not constrain the result here. + )?; + assert_eq!(magnitude_zs.len(), magnitude_words + 1); + lookup_config.copy_short_check( + layouter.namespace(|| "magnitude highest 4 bits"), + magnitude_zs[magnitude_words].clone(), + magnitude_extra_bits, // The 7th window must be a 4 bits value. + )?; + } + + // Multiply asset by magnitude, using the long scalar mul. + // TODO: implement a new variable base multiplication which is optimized for 64-bit scalar + // (the long scalar mul is optimized for pallas::Base scalar (~255-bits)) + // + // magnitude_asset = [magnitude] asset + let magnitude_asset = { + let magnitude_scalar = ScalarVar::from_base( + ecc_chip.clone(), + layouter.namespace(|| "magnitude"), + &v_net_magnitude_sign.0, + )?; + let (magnitude_asset, _) = params.asset_base.mul( + layouter.namespace(|| "[magnitude] asset_base"), + magnitude_scalar, + )?; + magnitude_asset + }; + + // commitment = [sign] magnitude_asset = [v_net_magnitude_sign] asset + magnitude_asset.mul_sign( + layouter.namespace(|| "[sign] magnitude_asset"), + &v_net_magnitude_sign.1, + )? + } + }; + + // blind = [rcv] ValueCommitR + let (blind, _rcv) = { + let value_commit_r = OrchardFixedBasesFull::ValueCommitR; + let value_commit_r = FixedPoint::from_inner(ecc_chip, value_commit_r); + + // [rcv] ValueCommitR + value_commit_r.mul(layouter.namespace(|| "[rcv] ValueCommitR"), rcv)? + }; + + // [v_net_magnitude_sign] asset_base + [rcv] ValueCommitR + commitment.add(layouter.namespace(|| "cv"), &blind) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + circuit::{ + gadget::assign_free_advice, + value_commit_orchard::{gadgets::value_commit_orchard, ZsaValueCommitParams}, + K, + }, + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, + note::AssetBase, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + }; + use halo2_gadgets::{ + ecc::{ + chip::{CircuitVersion, EccChip, EccConfig}, + NonIdentityPoint, ScalarFixed, + }, + sinsemilla::chip::{SinsemillaChip, SinsemillaConfig}, + utilities::lookup_range_check::{ + LookupRangeCheck4_5BConfig, PallasLookupRangeCheck4_5BConfig, + }, + }; + + use group::Curve; + use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::MockProver, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance}, + }; + use pasta_curves::pallas; + + use rand::{rngs::OsRng, RngCore}; + + #[test] + fn test_value_commit_orchard_zsa() { + #[derive(Clone, Debug)] + pub struct MyConfig { + primary: Column, + advices: [Column; 10], + ecc_config: EccConfig, + // Sinsemilla config is only used to initialize the table_idx lookup table in the same + // way as in the Orchard circuit + sinsemilla_config: SinsemillaConfig< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + PallasLookupRangeCheck4_5BConfig, + >, + } + #[derive(Default)] + struct MyCircuit { + v_old: Value, + v_new: Value, + rcv: Value, + asset: Value, + split_flag: Value, + } + + impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let advices = [ + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + meta.advice_column(), + ]; + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + + // Instance column used for public inputs + let primary = meta.instance_column(); + meta.enable_equality(primary); + + let table_idx = meta.lookup_table_column(); + let table_range_check_tag = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + ); + + let lagrange_coeffs = [ + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + meta.fixed_column(), + ]; + meta.enable_constant(lagrange_coeffs[0]); + + let range_check = LookupRangeCheck4_5BConfig::configure_with_tag( + meta, + advices[9], + table_idx, + table_range_check_tag, + ); + + let sinsemilla_config = SinsemillaChip::configure( + meta, + advices[..5].try_into().unwrap(), + advices[6], + lagrange_coeffs[0], + lookup, + range_check, + true, + ); + + MyConfig { + primary, + advices, + ecc_config: + EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ), + sinsemilla_config, + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + // Load the Sinsemilla generator lookup table. + SinsemillaChip::load(config.sinsemilla_config.clone(), &mut layouter)?; + + // Construct an ECC chip + let ecc_chip = EccChip::construct(config.ecc_config, CircuitVersion::AnchoredBase); + + let sinsemilla_chip = SinsemillaChip::construct(config.sinsemilla_config.clone()); + + // Witness the magnitude and sign of v_net = v_old - v_new + let v_net_magnitude_sign = { + // v_net is equal to + // (-v_new) if split_flag = true + // v_old - v_new if split_flag = false + let v_net = self.split_flag.and_then(|split_flag| { + if split_flag { + Value::known(crate::value::NoteValue::ZERO) - self.v_new + } else { + self.v_old - self.v_new + } + }); + + let magnitude_sign = v_net.map(|v_net| { + let (magnitude, sign) = v_net.magnitude_sign(); + ( + // magnitude is guaranteed to be an unsigned 64-bit value. + // Therefore, we can move it into the base field. + pallas::Base::from(magnitude), + match sign { + crate::value::Sign::Positive => pallas::Base::one(), + crate::value::Sign::Negative => -pallas::Base::one(), + }, + ) + }); + + let magnitude = assign_free_advice( + layouter.namespace(|| "v_net magnitude"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.0), + )?; + let sign = assign_free_advice( + layouter.namespace(|| "v_net sign"), + config.advices[9], + magnitude_sign.map(|m_s| m_s.1), + )?; + (magnitude, sign) + }; + + // Witness rcv + let rcv = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "rcv"), + self.rcv.as_ref().map(|rcv| rcv.inner()), + )?; + + // Witness asset + let asset_base = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness asset"), + self.asset.map(|asset| asset.cv_base().to_affine()), + )?; + + // Evaluate cv_net with value_commit_orchard + let cv_net = value_commit_orchard( + layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net)"), + ecc_chip, + v_net_magnitude_sign, + rcv, + Some(ZsaValueCommitParams { + sinsemilla_chip, + asset_base, + }), + )?; + + // Constrain cv_net to equal public input + layouter.constrain_instance(cv_net.inner().x().cell(), config.primary, 0)?; + layouter.constrain_instance(cv_net.inner().y().cell(), config.primary, 1) + } + } + + // Test different circuits + let mut rng = OsRng; + let mut circuits = vec![]; + let mut instances = vec![]; + let zatoshi_asset = AssetBase::zatoshi(); + let random_asset = AssetBase::random(&mut rng); + for split_flag in [false, true] { + for asset in [zatoshi_asset, random_asset] { + let v_old = NoteValue::from_raw(rng.next_u64()); + let v_new = NoteValue::from_raw(rng.next_u64()); + let rcv = ValueCommitTrapdoor::random(&mut rng); + let v_net = if split_flag { + NoteValue::ZERO - v_new + } else { + v_old - v_new + }; + circuits.push(MyCircuit { + v_old: Value::known(v_old), + v_new: Value::known(v_new), + rcv: Value::known(rcv.clone()), + asset: Value::known(asset), + split_flag: Value::known(split_flag), + }); + let expected_cv_net = ValueCommitment::derive_with_asset(v_net, rcv, asset); + instances.push([[expected_cv_net.x(), expected_cv_net.y()]]); + } + } + + for (circuit, instance) in circuits.iter().zip(instances.iter()) { + let prover = MockProver::::run( + K, + circuit, + instance.iter().map(|p| p.to_vec()).collect(), + ) + .unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + } +} diff --git a/src/circuit_data/circuit_description_zsa b/src/circuit_data/circuit_description_zsa new file mode 100644 index 000000000..e712e35a0 --- /dev/null +++ b/src/circuit_data/circuit_description_zsa @@ -0,0 +1,29042 @@ +PinnedVerificationKey { + base_modulus: "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + scalar_modulus: "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + domain: PinnedEvaluationDomain { + k: 11, + extended_k: 14, + omega: 0x181b50ad5f32119e31cbd395426d600b7a9b88bcaaa1c24eef28545aada17813, + }, + cs: PinnedConstraintSystem { + num_fixed_columns: 33, + num_advice_columns: 10, + num_instance_columns: 1, + num_selectors: 60, + gates: [ + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Negated( + Constant( + 0x2f70597a8e3d0f42f7a86a704f9bb232fe04a37f2b5a7c8c2aa7bd6e3af94367, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + Negated( + Constant( + 0x2d0e5169311919af1e917f63136d6c421d9ea766a7ffe3dba413c47eaf5af28e, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Negated( + Constant( + 0x2f70597a8e3d0f42f7a86a704f9bb232fe04a37f2b5a7c8c2aa7bd6e3af94367, + ), + ), + ), + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + Negated( + Constant( + 0x2d0e5169311919af1e917f63136d6c421d9ea766a7ffe3dba413c47eaf5af28e, + ), + ), + ), + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Sum( + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 21, + column_index: 21, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Sum( + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Product( + Sum( + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Product( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Product( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 22, + column_index: 22, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Product( + Sum( + Scaled( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Product( + Sum( + Scaled( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Negated( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Product( + Sum( + Scaled( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Product( + Sum( + Scaled( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Product( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 22, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 19, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 22, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Product( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + Product( + Constant( + 0x0000000000000000000000000000000010000000000000000000000000000000, + ), + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc0994a8dd8c46eb2100000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000010000000000000000000000000000000, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 23, + column_index: 23, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + ), + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000000, + ), + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Fixed { + query_index: 0, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Sum( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + Fixed { + query_index: 10, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Fixed { + query_index: 3, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + ), + ), + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000000, + ), + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Fixed { + query_index: 0, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 10, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Fixed { + query_index: 3, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Product( + Product( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + ), + ), + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + Constant( + 0x0000000000000000000000000000000001000000000000000000000000000000, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Product( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + 0x1000000000000000000000000000000000000000000000000000000000000000, + ), + ), + ), + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Negated( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x233162630ebf9ed7f8e24f66822c2d9f3a0a464048bd770ad049cdc8d085167c, + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x25cae2599892a8b0b36664548d60957d78f8365c85bbab07402270113e047a2e, + ), + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x22f5b5e1e6081c9774938717989a19579aad3d8262efd83ff84d806f685f747a, + ), + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x2e29dd59c64b1037f333aa91c383346421680eabc56bc15dfee7a9944f84dbe4, + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x1d1aab4ec1cd678892d15e7dceef1665cbeaf48b3a0624c3c771effa43263664, + ), + ), + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x3bf763086a18936451e0cbead65516b975872c39b59a31f615639415f6e85ef1, + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Product( + Product( + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0ab5e5b874a68de7b3d59fbdc8c9ead497d7a0ab23850b56323f2486d7e11b63, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x2cc057f3fa14687acc59ffd00de864434543705f35e98ab5c6de463cd1404e6b, + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x32e7c439f2f967e55fd72b55df208385fadbf8ae7ae24796171840417cab7576, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x2eae5df8c3115969f461778abf6c91fa1403db6f50302040942645bd7d4464e0, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x233162630ebf9ed7f8e24f66822c2d9f3a0a464048bd770ad049cdc8d085167c, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x25cae2599892a8b0b36664548d60957d78f8365c85bbab07402270113e047a2e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x22f5b5e1e6081c9774938717989a19579aad3d8262efd83ff84d806f685f747a, + ), + ), + Fixed { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x07bf368481067199db18b4aefe68d26d13f074fde9a18b29a1ca1516a4a1a6a0, + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x2aec6906c63f3cf1018a918b9dac5dadbb1d65040c85c1bfe82425bc1b23a059, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0952e0243aec2af01215944a64a246b276b2a7139db71b36e0541adf238e0781, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x2e29dd59c64b1037f333aa91c383346421680eabc56bc15dfee7a9944f84dbe4, + ), + Scaled( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x1d1aab4ec1cd678892d15e7dceef1665cbeaf48b3a0624c3c771effa43263664, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x3bf763086a18936451e0cbead65516b975872c39b59a31f615639415f6e85ef1, + ), + ), + Fixed { + query_index: 10, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x2fcbba6f9159a219723a63a0c09dab26aef9112e952fdbb52a418d8d73a7c908, + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x1ec7372574f3851bb4ddd4b4d6452256c5e4960d7424cd3776efab42d4fba90b, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0d0c2efd6472f12a3c26fa4b7d25b1e487a7435d30f8be81adc8933c6f3c72ee, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 24, + column_index: 24, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Scaled( + Advice { + query_index: 22, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + ), + ), + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Scaled( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Sum( + Sum( + Scaled( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Product( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Product( + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Scaled( + Product( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Negated( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Scaled( + Sum( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Scaled( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + 0x0001000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Scaled( + Advice { + query_index: 21, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + ), + ), + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Negated( + Sum( + Sum( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Product( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Product( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Scaled( + Product( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Negated( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Scaled( + Sum( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + 0x0001000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000200, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + 0x0400000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + 0x0020000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 10, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 11, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + 0x0400000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 29, + column_index: 29, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000100, + ), + ), + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000400000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000200, + ), + ), + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0200000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000200, + ), + ), + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0400000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 30, + column_index: 30, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + Scaled( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + 0x0400000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 31, + column_index: 31, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000100, + ), + ), + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000400000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000010, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000200, + ), + ), + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0200000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000200, + ), + ), + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 16, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + 0x0400000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x4000000000000000000000000000000000000000000000000000000000000000, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Constant( + 0x0000000000000000000000000000000400000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 20, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 32, + column_index: 32, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + ], + advice_queries: [ + ( + Column { + index: 0, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 1, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 2, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 3, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 4, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 5, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 6, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 7, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 8, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 9, + column_type: Advice, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 0, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 1, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 2, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 3, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 4, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 5, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 6, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 7, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 9, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 9, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 8, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 6, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 1, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 7, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 8, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ], + instance_queries: [ + ( + Column { + index: 0, + column_type: Instance, + }, + Rotation( + 0, + ), + ), + ], + fixed_queries: [ + ( + Column { + index: 3, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 0, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 11, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 12, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 4, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 5, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 6, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 7, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 8, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 9, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 10, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 13, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 1, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 2, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 14, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 15, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 16, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 17, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 18, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 19, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 20, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 21, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 22, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 23, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 24, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 25, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 26, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 27, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 28, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 29, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 30, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 31, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 32, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ], + permutation: Argument { + columns: [ + Column { + index: 0, + column_type: Instance, + }, + Column { + index: 0, + column_type: Advice, + }, + Column { + index: 1, + column_type: Advice, + }, + Column { + index: 2, + column_type: Advice, + }, + Column { + index: 3, + column_type: Advice, + }, + Column { + index: 4, + column_type: Advice, + }, + Column { + index: 5, + column_type: Advice, + }, + Column { + index: 6, + column_type: Advice, + }, + Column { + index: 7, + column_type: Advice, + }, + Column { + index: 8, + column_type: Advice, + }, + Column { + index: 9, + column_type: Advice, + }, + Column { + index: 3, + column_type: Fixed, + }, + Column { + index: 8, + column_type: Fixed, + }, + Column { + index: 9, + column_type: Fixed, + }, + Column { + index: 10, + column_type: Fixed, + }, + ], + }, + lookups: [ + Argument { + input_expressions: [ + Product( + Fixed { + query_index: 15, + column_index: 15, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 18, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Product( + Fixed { + query_index: 15, + column_index: 15, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + ), + Product( + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 2, + column_index: 11, + rotation: Rotation( + 0, + ), + }, + ], + }, + Argument { + input_expressions: [ + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Product( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 11, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x0db5218be6881f0f1431d4ea7d4afc7b29a05bafbede62b55a91eb912044ea5f, + ), + ), + Sum( + Product( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Scaled( + Product( + Sum( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 4, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + Negated( + Product( + Advice { + query_index: 3, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x2f0f40c2f152a01c9caf66298493d5d0944a041c2e65ba0117c24f76bf8e6483, + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 12, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 13, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ], + }, + Argument { + input_expressions: [ + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Negated( + Product( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Advice { + query_index: 17, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x0db5218be6881f0f1431d4ea7d4afc7b29a05bafbede62b55a91eb912044ea5f, + ), + ), + Sum( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Scaled( + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + Negated( + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ), + ), + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x2f0f40c2f152a01c9caf66298493d5d0944a041c2e65ba0117c24f76bf8e6483, + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 12, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 13, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ], + }, + ], + constants: [ + Column { + index: 3, + column_type: Fixed, + }, + ], + minimum_degree: None, + }, + fixed_commitments: [ + (0x3887a29815d3a28d43c453904169e07def787baca6c9452505d2de78dd44b86f, 0x2f95e59472ab087725d62deea9c3b406ae490eff7c0318269d6f2b025934cc70), + (0x285e16cead69d5fef9441d4d4ac695fe8880120d209e1e631d8a4bc5f55e90e6, 0x3700fd2dfcfac5b2cb6d33c60ac79dfac3fb95518cd0aa97c75a51d62281f2c1), + (0x2dc11f4f0000da15c59cd395ea5f32b4e79783d958c290c821c89e0bcb50eaf1, 0x2c53e12ce0a2022516ef31f0224b0bc1b119861078358f78d245ba5d2f180099), + (0x12687070faf8f4f105a9d934d19483fa5a6408f89da99e3a9fd4be62e0dfc096, 0x2ffb3c9d30a5018e92a6a16d0c4497c7e6c2351ab8b1cf843025aa8296a7a1b0), + (0x2c3cb3018e95bebcd68f91acdca621c5256a0646410e7749b21762e12ac4c59c, 0x0729ba4393b5a1aee9613084bf8c568bca6ab570a854f915ff3ebe749329bfb2), + (0x071dd89dd211843a4a43b0cec253110276129a57fc7eae27157173c7734f1a35, 0x237d9c92ab72dd806fa5c89acf79756e8dff9f9e1a084a52b634cc6abd3c4343), + (0x3e1c518dddcb414fd054eab3c299e2015f0529f222e250de32b707fb091e5354, 0x1ac07dcdbb24dd995fe15c35b3e431b642f08a2a8a120fa29c7d923fc8895a90), + (0x3a7f2d89eaeceb9d4ff99b8f39e9e61b541ed0cd0a813db9774b44abe9b398e9, 0x024133f27b23b813466224229d59a404e4d1aea665e7899f286bfa7b8561ecdd), + (0x38733ece2d3436b470f9a7dff444fe8d858be85223b54da1ec1e9d1b4c61c9c2, 0x3c2aca293f329e3ee71e1f83626412b1731e9ad04f214729d52b5ebd79301e28), + (0x00422e9ab307e8d087a1c85332be8a109666fb84d6668cd75185858a3f9831db, 0x1304e4e05cdc11622cf2e2c9474650606e4f206c6112b2f83e9a67f21091d3e9), + (0x30035a704d95a29ae9e92f4d10214d5301bdf6a48ebf84bd7f97c4be3fe7b1e6, 0x191e6980af76303cd30f83463cd46c7f21efb6216843b5f3020b90eec034b17e), + (0x0decb727cf32e573604ae3928a761b450d9460f2a7283c8cf88a2b38b796e575, 0x044c0a88bc6aa468e75fc709f9eb21b6e309dd8a6ca62f7a3da86299141ea3a9), + (0x026f2d4f33762588e5761db208ab5389af2f574497a4c964e85b85333ef8db08, 0x135a1123b8e23011411aca9ca7872e5ccb9859c6e3e6fc49dd3abbbfba091838), + (0x230edab6252bf684c70b5483ac87c1544611802e2fe97b06c020749aaf778932, 0x21d63edb865738b56a5d68a90c941aae436dfcd80c9246e2a330a974f5ae9c98), + (0x3b09db3011d8047afc6ea9c8f3787f9b1870affc14ab64a9433508050cd41d9f, 0x3903a28dcf729c1132353036fc2819a0d0dce6ceda266a388ae401026274e99a), + (0x11319dc725bdd4b8a9464754ac33554d4b71725a65c90038fe61626264fb6388, 0x03a1d616415b451baaa4ce287af9efff01ee7d96322bccdedb015a00faee6023), + (0x18b67544d4466429760be2b231418b43d35d79462f8743051c8e437dd710035d, 0x2ca31a6106310316da5633fe4e16e2a0affafc4541b280a9722ae86219127aa0), + (0x20d37f384756b3a7335aef06267be85fb86bc0cf001fc9e5d3f77c79b1143988, 0x219649544c0fea9756d7cf5dc97f2d8cbfd7c9d77d7bdef74b56b3f922cf9fa0), + (0x0c933e9fb63e87a6bbfae6112fee8302ba6a692463cdbf4f078b3d6c44c989c5, 0x0036dbb46e3cc69bd3777610d4ca07553bf370341608447aa455fc71bfaea29f), + (0x09902e130674063b42dfef8cd7eb3ee29cf24d339e5a71cf345e4b3496f4f8b2, 0x0547528142228da79e79ab67f98c3a39e3ea9a09281441e55811d75614b9d32b), + (0x12da47c8091b8de4fc979e49f7b173d352504f18ccc461ecc5bbe4a798078ba8, 0x2d7eb25b36bb9419d081f457f4424ffc9c3b47d558063bc5c58de550c041cdf9), + (0x05cb05699089f46ed5e0c6cf305a6ca1b6bc059a2bc0d76b466e099941a95e5a, 0x106a3f2dd910945b9f15ff194bd5b20d2f38c0feaab08a31d88b6fc9c2c13578), + (0x0dd17e3bf4b9e0f4ac36b1cb4d3808120a042ef99e59efcad62794c3a2ce9228, 0x2131fb87d09400d09bfc4001ec504173ed14e1a33d0c4e21315fbfb991f51be7), + (0x334105dd96996a08df56e88261dcd87c5356cfc0f2b8ae0a9419899d5def147b, 0x09ddf24ae765eda056cf59b16ac14464240c83e253135682c02e6546f89dd396), + (0x306673a286cda5b43370c6515d374daf42ec53e7a02d35d60500bcc8427f17f8, 0x3a8f47aee865915bfa005b5bbf253dad709d55a8cc46b22f52b2a181793e15a8), + (0x0b89c282c8f2d54e66c7a11e6b4338a2f06a7c9430f7d3376328f12fa001cac5, 0x098b5c8ee6548183ccb4e60cdfee10b509db34ed136daf3530a426e8f200e89d), + (0x2f7f85b86b6adcf714d0df7878f4604d33fd319f39bd00fd5c34378247a155b5, 0x3a2f025447b83b65d148e99c93fe8bb991a81ef6d2f827f010680467361c47fc), + (0x253dd5b2034ec775feee51f34147604077e6f18fcc89f2f929542e7589adce76, 0x3a690122dec15d670bc5d464e2e920fba47c6cf84becd482732f284d7a147c28), + (0x32dfd92286d3ccf6460d9562514d2981460c63258125e9e09371d5d3aa4b17dc, 0x0bc267fa9499bf44ec29d938a343690f49f557515c158844a6ad535eb9341293), + (0x376d1a671db3cc9b0f44dfa8b3bb2e35e591285f68a953fd409b6df1f3e3719f, 0x3bb71f0675a75bfa90f852f84139c599ba530e57d3dd373e54d07d8adb2e2279), + (0x2b6ab63da764c4348fe5cbfc40b6665e70559be6e2fd21769149dab876ab5e32, 0x07db6fabd4ccbdecf13aa94b0a4f5ec8b5822af4179151fe94dbb2c6bbdedb56), + (0x0aeba285a6fee58cc04b7c10023964dbdc9f938d0a0e5a88bb9066e0359298c7, 0x31e916eb6d95ba73a7a6b95650f9bb90693be229706f4c92965d152cebf4d0eb), + (0x2140e3581500582c2d57bbd963a42439685e94ecf9e394a7785921f6caf468b3, 0x00880d404684eb34f89085748031a279994e4cc749562bec0fd63dcdcaae9169), + ], + permutation: VerifyingKey { + commitments: [ + (0x1f971d009215bc1231c62b232ebd70a71615df8505c0a01281ff3c49ef7d56a2, 0x0711468515efff1c9a396af4f2c5f7db6470ea59d114ed10022521a63981cdcd), + (0x1989a4ce91f98767ff92d866e44a0a49d7c6fc31980626acccfb998e7b3793be, 0x0292890e32a077d4a065db79b4ea1854e8d175c670070cb872a30e73c0efcba0), + (0x12453b8434b3219d0279aaeaaddc291e091779c707106668904c0ffd83841aa7, 0x270b9525d47ba2c36be906393b3402a29e723156f71d053252965d62549d6ca4), + (0x3025eb5cabbe697313089148d725e3fca3982b52708dc2c253abf7a2e481550d, 0x2c004bdaef66338d84cf270b2c528a73eb5739742d704f53d44aaedbe4c1bca4), + (0x1b11bf1e11b41c9f3148e4badd351f8c3e839b85766706c14b55db06b79169d4, 0x04001319d3fb9e8616cb9b95768344321db9f9056520ab2bae834516454c58d5), + (0x30c590c52778772de4290c9ad505030414b39cbcb1fd88122265270af4568fed, 0x349e0d2631738ed53cca28acd9efd700e500e83e2f3bbef9c55d5c938f5d2d00), + (0x308000b1910f35aa80072335e375bb6d203df1b91da15c3da928f81720119760, 0x38cafe9c4bac8f9d371395c097e878c6b9dda15ba992a62266008eb8bc2b3e90), + (0x35427b5e0a9b366a41427cd2fdcfd061c0c1725195084c47d8ca853712ad4876, 0x2a98be529a07d98d3db274448f929226b05924aa51bf2e2c5b089a930f8d886c), + (0x02a15f25b60724f949a9558b84fdbc706e237205639b20369eb5cc106c03f207, 0x364049b7cfb635e7f4909174bd6794cd0b62cb17789574fd3b8fa97d1459ac75), + (0x28ec717c7371da7496484cd22b4d82336739b22d4610ba620709a48356650185, 0x3e85814a136a98f38f86cac3488f22759d81e3a5b9308b753a57f45a1fbd325b), + (0x3a1a77447e988bc7bd990a87721724fec5264ecdd3b48d289d82450bc1ae1eec, 0x29c4e88dabcc6807bc9934242c230dcc04f322b74868295f1521a8cd13fc2461), + (0x15398705d646d620b46dceb5c06e01628fc86a3615db4af55b4400b958d16251, 0x305743bb3ad1fe6e0d6ed34b1ac014a52fec58874226130a8bb7f9190f593ccf), + (0x21d210b41675a1eae44cbd0f3fd27d69e30716c71873f6089cee61acacd403ab, 0x2275e97c7e84f68bfaa528a9d8be4e059f7abefd80d03fbfca774e8414a9b7c1), + (0x0f9e7de28e0f650d99d99d95c0fcd39c9dac9db5aa1973319f66922d6eb9f7d5, 0x1ba644ecc18ad711ddd33af7f695f6834e9f35c93d47a6a5273dabbe800fc7e6), + (0x0aab3ab73afac76277cd94a891de15e42ceb09f3a9865dab5c814bebfbb4453f, 0x27119fec3736d99abeeef1ad7b857db7e754e0c158780ed3dd0cdd4dc2453e10), + ], + }, +} diff --git a/src/circuit_version.rs b/src/circuit_version.rs new file mode 100644 index 000000000..b0740bd2f --- /dev/null +++ b/src/circuit_version.rs @@ -0,0 +1,61 @@ +//! [`OrchardCircuitVersion`] and its proof-size logic. +//! +//! Kept outside the `circuit` module so proof-size validation stays available without the +//! `circuit` feature flag. + +/// Selects which version of the Orchard Action circuit to build. +/// +/// [`FixedPostNu6_2`] and [`InsecurePreNu6_2`] produce different verifying keys: the fixed +/// circuit anchors the variable-base scalar-multiplication base (see `halo2_gadgets`), while +/// the pre-NU6.2 one does not. [`PostNu6_3`] extends the fixed circuit by enforcing the +/// same-address check, i.e. `(g_d^old, pk_d^old) = (g_d^new, pk_d^new)`, when the +/// boolean `disableCrossAddress` public input is set. +/// +/// This is a runtime value rather than a type parameter: it is carried in [`Circuit`] and +/// chosen when building a [`ProvingKey`] or [`VerifyingKey`], so the circuit version can be +/// threaded dynamically (e.g. across an FFI boundary). +/// +/// Please note that the public exposure of APIs using `InsecurePreNu6_2` is intentional, +/// and is strictly necessary for verifying the block chain from NU5 activation and for +/// creating proofs needed by tests that operate at past epochs. These APIs cannot be +/// used accidentally without passing an `OrchardCircuitVersion` that is clearly labelled +/// "insecure". This is not a security vulnerability. +/// +/// [`FixedPostNu6_2`]: OrchardCircuitVersion::FixedPostNu6_2 +/// [`InsecurePreNu6_2`]: OrchardCircuitVersion::InsecurePreNu6_2 +/// [`PostNu6_3`]: OrchardCircuitVersion::PostNu6_3 +/// [`Circuit`]: crate::circuit::Circuit +/// [`ProvingKey`]: crate::circuit::ProvingKey +/// [`VerifyingKey`]: crate::circuit::VerifyingKey +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum OrchardCircuitVersion { + /// The insecure pre-NU6.2 circuit, in which the variable-base scalar-multiplication base + /// is not anchored to the real base. For reconstructing the historical (NU5..NU6.2) + /// verifying key only — never for proving or current verification. + InsecurePreNu6_2, + /// The fixed circuit, active from NU6.2 onward. Used for all current network proving and + /// verification. + FixedPostNu6_2, + /// The post-NU 6.3 circuit. This uses the fixed circuit with additional constraints + /// enforcing the `disableCrossAddress` public input. + PostNu6_3, + /// The OrchardZSA circuit, defined in [ZIP-226]. + /// + /// [ZIP-226]: https://zips.z.cash/zip-0226 + ZSA, +} + +impl OrchardCircuitVersion { + /// Returns the `(base, per_action)` proof-size constants for the circuit that proves and + /// verifies actions for this bundle version, such that a canonical proof for `num_actions` + /// actions is exactly `base + per_action * num_actions` bytes. + pub(crate) const fn proof_size_constants(&self) -> (usize, usize) { + match self { + OrchardCircuitVersion::InsecurePreNu6_2 + | OrchardCircuitVersion::FixedPostNu6_2 + | OrchardCircuitVersion::PostNu6_3 => (2720, 2272), + OrchardCircuitVersion::ZSA => (2848, 2272), + } + } +} diff --git a/src/constants.rs b/src/constants.rs index be7024ceb..cbf02533f 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,9 @@ //! Constants used in the Orchard protocol. pub mod fixed_bases; +pub mod nullifier_l; pub mod sinsemilla; pub mod util; +pub mod zatoshi_asset_base; #[cfg(feature = "circuit")] pub use self::sinsemilla::{OrchardCommitDomains, OrchardHashDomains}; diff --git a/src/constants/fixed_bases.rs b/src/constants/fixed_bases.rs index aadb5fdca..e37c890a8 100644 --- a/src/constants/fixed_bases.rs +++ b/src/constants/fixed_bases.rs @@ -43,6 +43,9 @@ pub const VALUE_COMMITMENT_R_BYTES: [u8; 1] = *b"r"; /// SWU hash-to-curve personalization for the note commitment generator pub const NOTE_COMMITMENT_PERSONALIZATION: &str = "z.cash:Orchard-NoteCommit"; +/// SWU hash-to-curve personalization for the ZSA note commitment generator +pub const NOTE_ZSA_COMMITMENT_PERSONALIZATION: &str = "z.cash:ZSA-NoteCommit"; + /// SWU hash-to-curve personalization for the IVK commitment generator pub const COMMIT_IVK_PERSONALIZATION: &str = "z.cash:Orchard-CommitIvk"; diff --git a/src/constants/nullifier_l.rs b/src/constants/nullifier_l.rs new file mode 100644 index 000000000..4a7e42afd --- /dev/null +++ b/src/constants/nullifier_l.rs @@ -0,0 +1,44 @@ +//! $\mathcal{L}^{\mathsf{Orchard}}$ constant. +//! +//! This constant is used to evaluate the nullifier of a split note (ZIP 226). + +use pasta_curves::{arithmetic::CurveAffine, pallas}; + +/// Constant used as $\mathcal{L}^{\mathsf{Orchard}}$ in DeriveNullifier. +pub const NULLIFIER_L: (pallas::Base, pallas::Base) = ( + pallas::Base::from_raw([ + 0x7956_0ba9_2372_5d67, + 0xcb79_efc2_91c1_b0c8, + 0xbbed_d606_d1df_737e, + 0x032e_6e8c_f01d_3ea1, + ]), + pallas::Base::from_raw([ + 0x8838_95de_f68a_1265, + 0xd861_4d8d_ab43_e39e, + 0xf9e6_ebb2_b7b5_6640, + 0x093c_af7e_c368_376e, + ]), +); + +pub(crate) fn nullifier_l() -> pallas::Affine { + pallas::Affine::from_xy(NULLIFIER_L.0, NULLIFIER_L.1).unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + use group::Curve; + use pasta_curves::arithmetic::CurveAffine; + use pasta_curves::arithmetic::CurveExt; + + #[test] + fn nullifier_l() { + let expected_nullifier_l = pallas::Point::hash_to_curve("z.cash:Orchard")(b"L") + .to_affine() + .coordinates() + .unwrap(); + + assert_eq!(*expected_nullifier_l.x(), NULLIFIER_L.0); + assert_eq!(*expected_nullifier_l.y(), NULLIFIER_L.1); + } +} diff --git a/src/constants/sinsemilla.rs b/src/constants/sinsemilla.rs index 1f4fb9f28..8107b8057 100644 --- a/src/constants/sinsemilla.rs +++ b/src/constants/sinsemilla.rs @@ -40,6 +40,18 @@ pub const Q_NOTE_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); +/// Generator used in SinsemillaHashToPoint for ZSA note commitment +pub const Q_NOTE_ZSA_COMMITMENT_M_GENERATOR: ([u8; 32], [u8; 32]) = ( + [ + 207, 235, 191, 45, 66, 225, 8, 126, 199, 188, 39, 26, 115, 106, 18, 2, 191, 173, 75, 9, 65, + 225, 175, 193, 224, 202, 228, 177, 3, 75, 228, 1, + ], + [ + 220, 251, 80, 86, 182, 182, 99, 67, 254, 97, 241, 22, 79, 111, 161, 176, 79, 97, 208, 98, + 116, 57, 110, 196, 25, 73, 239, 31, 196, 97, 19, 30, + ], +); + /// Generator used in SinsemillaHashToPoint for IVK commitment pub const Q_COMMIT_IVK_M_GENERATOR: ([u8; 32], [u8; 32]) = ( [ @@ -80,9 +92,12 @@ pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] { /// The Sinsemilla hash domains used in Orchard. #[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum OrchardHashDomains { /// The domain used by the `NoteCommit` commitment's internal Sinsemilla hash. NoteCommit, + /// The domain used by the `NoteZsaCommit` commitment's internal Sinsemilla hash. + NoteZsaCommit, /// The domain used by the `CommitIvk` commitment's internal Sinsemilla hash. CommitIvk, /// The domain used by the Orchard Merkle tree's `MerkleCRH^Orchard` hash. @@ -104,6 +119,11 @@ impl HashDomains for OrchardHashDomains { pallas::Base::from_repr(Q_NOTE_COMMITMENT_M_GENERATOR.1).unwrap(), ) .unwrap(), + OrchardHashDomains::NoteZsaCommit => pallas::Affine::from_xy( + pallas::Base::from_repr(Q_NOTE_ZSA_COMMITMENT_M_GENERATOR.0).unwrap(), + pallas::Base::from_repr(Q_NOTE_ZSA_COMMITMENT_M_GENERATOR.1).unwrap(), + ) + .unwrap(), OrchardHashDomains::MerkleCrh => pallas::Affine::from_xy( pallas::Base::from_repr(Q_MERKLE_CRH.0).unwrap(), pallas::Base::from_repr(Q_MERKLE_CRH.1).unwrap(), @@ -115,9 +135,12 @@ impl HashDomains for OrchardHashDomains { /// The Sinsemilla commitment domains used in Orchard. #[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] pub enum OrchardCommitDomains { /// The `NoteCommit` commitment domain. NoteCommit, + /// The `NoteCommit` commitment domain for ZSA. + NoteZsaCommit, /// The `CommitIvk` commitment domain. CommitIvk, } @@ -127,6 +150,8 @@ impl CommitDomains for Or fn r(&self) -> OrchardFixedBasesFull { match self { Self::NoteCommit => OrchardFixedBasesFull::NoteCommitR, + // For ZSA note commitment, we use the same `R` than for ZEC note commitment. + Self::NoteZsaCommit => OrchardFixedBasesFull::NoteCommitR, Self::CommitIvk => OrchardFixedBasesFull::CommitIvkR, } } @@ -134,6 +159,7 @@ impl CommitDomains for Or fn hash_domain(&self) -> OrchardHashDomains { match self { Self::NoteCommit => OrchardHashDomains::NoteCommit, + Self::NoteZsaCommit => OrchardHashDomains::NoteZsaCommit, Self::CommitIvk => OrchardHashDomains::CommitIvk, } } @@ -143,7 +169,10 @@ impl CommitDomains for Or mod tests { use super::*; use crate::constants::{ - fixed_bases::{COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION}, + fixed_bases::{ + COMMIT_IVK_PERSONALIZATION, NOTE_COMMITMENT_PERSONALIZATION, + NOTE_ZSA_COMMITMENT_PERSONALIZATION, + }, sinsemilla::MERKLE_CRH_PERSONALIZATION, }; use group::{ff::PrimeField, Curve}; @@ -204,6 +233,25 @@ mod tests { ); } + #[test] + fn q_note_zsa_commitment_m() { + let domain = CommitDomain::new_with_separate_domains( + NOTE_ZSA_COMMITMENT_PERSONALIZATION, + NOTE_COMMITMENT_PERSONALIZATION, + ); + let point = domain.Q(); + let coords = point.to_affine().coordinates().unwrap(); + + assert_eq!( + *coords.x(), + pallas::Base::from_repr(Q_NOTE_ZSA_COMMITMENT_M_GENERATOR.0).unwrap() + ); + assert_eq!( + *coords.y(), + pallas::Base::from_repr(Q_NOTE_ZSA_COMMITMENT_M_GENERATOR.1).unwrap() + ); + } + #[test] fn q_commit_ivk_m() { let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION); diff --git a/src/constants/zatoshi_asset_base.rs b/src/constants/zatoshi_asset_base.rs new file mode 100644 index 000000000..07e737eab --- /dev/null +++ b/src/constants/zatoshi_asset_base.rs @@ -0,0 +1,49 @@ +//! Asset base of the zatoshi asset. +//! +//! Every protocol version other than OrchardZSA uses this asset base for all of its notes. + +use pasta_curves::{arithmetic::CurveAffine, pallas}; + +/// Constant used as the asset base of the zatoshi asset. +pub(crate) const ZATOSHI_ASSET_BASE: (pallas::Base, pallas::Base) = ( + pallas::Base::from_raw([ + 0x2aa7_bd6e_3af9_4367, + 0xfe04_a37f_2b5a_7c8c, + 0xf7a8_6a70_4f9b_b232, + 0x2f70_597a_8e3d_0f42, + ]), + pallas::Base::from_raw([ + 0xa413_c47e_af5a_f28e, + 0x1d9e_a766_a7ff_e3db, + 0x1e91_7f63_136d_6c42, + 0x2d0e_5169_3119_19af, + ]), +); + +pub(crate) fn zatoshi_asset_base() -> pallas::Affine { + pallas::Affine::from_xy(ZATOSHI_ASSET_BASE.0, ZATOSHI_ASSET_BASE.1).unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::constants::fixed_bases::{ + VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES, + }; + use group::Curve; + use pasta_curves::arithmetic::CurveAffine; + use pasta_curves::arithmetic::CurveExt; + + #[test] + fn zatoshi_asset_base() { + let expected_zatoshi_asset_base = pallas::Point::hash_to_curve( + VALUE_COMMITMENT_PERSONALIZATION, + )(&VALUE_COMMITMENT_V_BYTES) + .to_affine() + .coordinates() + .unwrap(); + + assert_eq!(*expected_zatoshi_asset_base.x(), ZATOSHI_ASSET_BASE.0); + assert_eq!(*expected_zatoshi_asset_base.y(), ZATOSHI_ASSET_BASE.1); + } +} diff --git a/src/lib.rs b/src/lib.rs index dc3e9cb75..d804ffc8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ pub mod builder; pub mod bundle; #[cfg(feature = "circuit")] pub mod circuit; +pub mod circuit_version; #[cfg(not(feature = "unstable-voting-circuits"))] mod constants; #[cfg(feature = "unstable-voting-circuits")] @@ -51,6 +52,7 @@ pub mod zip32; #[cfg(test)] mod test_vectors; +use crate::circuit_version::OrchardCircuitVersion; pub use action::{Action, ActionFromPartsError}; pub use address::Address; pub use bundle::Bundle; @@ -108,13 +110,15 @@ impl Proof { /// constructing a bundle from untrusted bytes; see [`Bundle::try_from_parts`]. /// /// [`Bundle::try_from_parts`]: crate::Bundle::try_from_parts - pub const fn expected_proof_size(num_actions: usize) -> usize { + pub const fn expected_proof_size( + circuit_version: OrchardCircuitVersion, + num_actions: usize, + ) -> usize { // The proof is a fixed base size plus a fixed contribution per action. These constants // are determined by the halo2 action circuit; see the `circuit` module's round-trip // tests, which cross-check them against `CircuitCost::proof_size`. - const BASE: usize = 2720; - const PER_ACTION: usize = 2272; - BASE + PER_ACTION * num_actions + let (base, per_action) = circuit_version.proof_size_constants(); + base + per_action * num_actions } } diff --git a/src/note.rs b/src/note.rs index b147f85d7..8a951e38d 100644 --- a/src/note.rs +++ b/src/note.rs @@ -1,4 +1,5 @@ //! Data structures used for note construction. +use alloc::vec::Vec; use core::fmt; use memuse::DynamicUsage; @@ -7,7 +8,7 @@ use ff::PrimeField; use group::GroupEncoding; use pasta_curves::pallas; use rand::RngCore; -use subtle::CtOption; +use subtle::{Choice, ConditionallySelectable, CtOption}; use crate::{ keys::{EphemeralSecretKey, FullViewingKey, Scope, SpendingKey}, @@ -16,6 +17,9 @@ use crate::{ Address, }; +pub(crate) mod asset_base; +pub use self::asset_base::AssetBase; + const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; const ZIP2005_ORCHARD_QR_RCM_DOMAIN_SEPARATOR: u8 = 0x0B; @@ -231,6 +235,17 @@ impl RandomSeed { } } +impl ConditionallySelectable for RandomSeed { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let result: Vec = + a.0.iter() + .zip(b.0.iter()) + .map(|(a_i, b_i)| u8::conditional_select(a_i, b_i, choice)) + .collect(); + RandomSeed(<[u8; 32]>::try_from(result).unwrap()) + } +} + /// A discrete amount of funds received by an address. #[derive(Debug, Copy, Clone)] pub struct Note { @@ -238,6 +253,8 @@ pub struct Note { recipient: Address, /// The value of this note. value: NoteValue, + /// The asset of this note. + asset: AssetBase, /// A unique creation ID for this note. /// /// This is produced from the nullifier of the note that will be spent in the [`Action`] that @@ -247,6 +264,10 @@ pub struct Note { rho: Rho, /// The seed randomness for various note components. rseed: RandomSeed, + /// The seed randomness for split notes. + /// + /// If it is not a split note, this field is `None`. + rseed_split_note: CtOption, /// The note plaintext version, determining rcm derivation strategy. version: NoteVersion, } @@ -286,8 +307,11 @@ impl Note { let note = Note { recipient, value, + // TODO ZSA: asset should be a param, not hardcoded here + asset: AssetBase::zatoshi(), rho, rseed, + rseed_split_note: CtOption::new(rseed, 0u8.into()), version, }; CtOption::new(note, note.commitment_inner().is_some()) @@ -356,11 +380,21 @@ impl Note { self.value } + /// Returns the asset of this note. + pub fn asset(&self) -> AssetBase { + self.asset + } + /// Returns the rseed value of this note. pub fn rseed(&self) -> &RandomSeed { &self.rseed } + /// Returns the rseed_split_note value of this note. + pub(crate) fn rseed_split_note(&self) -> CtOption { + self.rseed_split_note + } + /// Derives the ephemeral secret key for this note. pub(crate) fn esk(&self) -> EphemeralSecretKey { EphemeralSecretKey(self.rseed.esk(&self.rho)) @@ -381,6 +415,14 @@ impl Note { self.rseed.psi(&self.rho()) } + /// Derives the `psi_nf` value for this note. + /// + /// For a split note, this value comes from `rseed_split_note`. It is then different from + /// [`Self::psi`]. For all other notes, the two values are the same. + pub(crate) fn psi_nf(&self) -> pallas::Base { + self.rseed_split_note.unwrap_or(self.rseed).psi(&self.rho()) + } + /// Derives the note commitment trapdoor for this note. pub(crate) fn rcm(&self) -> commitment::NoteCommitTrapdoor { let rho = self.rho(); @@ -428,6 +470,7 @@ impl Note { g_d_bytes, pk_d_bytes, self.value, + self.asset, self.rho.0, psi, self.rcm(), @@ -436,7 +479,13 @@ impl Note { /// Derives the nullifier for this note. pub fn nullifier(&self, fvk: &FullViewingKey) -> Nullifier { - Nullifier::derive(fvk.nk(), self.rho.0, self.psi(), self.commitment()) + Nullifier::derive( + fvk.nk(), + self.rho.0, + self.psi_nf(), + self.commitment(), + self.rseed_split_note.is_some(), + ) } } @@ -469,9 +518,12 @@ pub mod testing { use proptest::prelude::*; use crate::{ - address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue, + address::testing::arb_address, note::nullifier::testing::arb_nullifier, note::AssetBase, + value::NoteValue, }; + use subtle::CtOption; + use super::{Note, NoteVersion, RandomSeed, Rho}; prop_compose! { @@ -491,8 +543,11 @@ pub mod testing { Note { recipient, value, + // TODO ZSA: asset should not be hardcoded here + asset: AssetBase::zatoshi(), rho, rseed, + rseed_split_note: CtOption::new(rseed, 0u8.into()), version, } } @@ -537,12 +592,28 @@ mod tests { let rho_inner = rho.into_inner(); let value = NoteValue::from_raw(tv.note_v); - let cmx_old = - NoteCommitment::derive(g_d_bytes, pk_d_bytes, value, rho_inner, psi, rcm_old).unwrap(); + let cmx_old = NoteCommitment::derive( + g_d_bytes, + pk_d_bytes, + value, + AssetBase::zatoshi(), + rho_inner, + psi, + rcm_old, + ) + .unwrap(); let cmx_old_bytes = ExtractedNoteCommitment::from(cmx_old).to_bytes(); - let cmx_qr = - NoteCommitment::derive(g_d_bytes, pk_d_bytes, value, rho_inner, psi, rcm_new).unwrap(); + let cmx_qr = NoteCommitment::derive( + g_d_bytes, + pk_d_bytes, + value, + AssetBase::zatoshi(), + rho_inner, + psi, + rcm_new, + ) + .unwrap(); let cmx_qr_bytes = ExtractedNoteCommitment::from(cmx_qr).to_bytes(); QrRcmDerivation { @@ -585,4 +656,41 @@ mod tests { ); } } + + /// A split note takes its psi from the split seed, and adds NULLIFIER_L to its nullifier. + /// No constructor sets `rseed_split_note`, so this test sets it directly. + #[test] + fn split_note_nullifier() { + let mut rng = rand::rngs::OsRng; + let (_, fvk, note) = Note::dummy(&mut rng, None, NoteVersion::V3); + let rho = note.rho(); + let split_rseed = RandomSeed::random(&mut rng, &rho); + let split_note = Note { + rseed_split_note: CtOption::new(split_rseed, 1u8.into()), + ..note + }; + + let psi_nf = split_rseed.psi(&rho); + assert_eq!(split_note.psi_nf(), psi_nf); + assert_ne!(psi_nf, note.psi()); + + let derive = |psi, is_split: bool| { + Nullifier::derive( + fvk.nk(), + rho.into_inner(), + psi, + note.commitment(), + Choice::from(u8::from(is_split)), + ) + }; + + // The split note uses the split seed's psi and adds NULLIFIER_L. + assert_eq!(split_note.nullifier(&fvk), derive(psi_nf, true)); + // Without NULLIFIER_L (is_split=false), the result is different. + assert_ne!(split_note.nullifier(&fvk), derive(psi_nf, false)); + // With the note's own psi, the result is different. + assert_ne!(split_note.nullifier(&fvk), derive(note.psi(), true)); + // A note with no split seed uses its own psi and does not add NULLIFIER_L (is_split=false). + assert_eq!(note.nullifier(&fvk), derive(note.psi(), false)); + } } diff --git a/src/note/asset_base.rs b/src/note/asset_base.rs new file mode 100644 index 000000000..8fa224595 --- /dev/null +++ b/src/note/asset_base.rs @@ -0,0 +1,82 @@ +use core::cmp::Ordering; +use group::{Group, GroupEncoding}; +use pasta_curves::pallas; +use subtle::{Choice, ConstantTimeEq, CtOption}; + +use crate::constants::zatoshi_asset_base::zatoshi_asset_base; + +#[cfg(test)] +use rand_core::CryptoRngCore; + +/// Note type identifier. +#[derive(Clone, Copy, Debug, Eq)] +pub struct AssetBase(pallas::Point); + +// AssetBase must implement PartialOrd and Ord to be used as a key in BTreeMap. +impl PartialOrd for AssetBase { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for AssetBase { + fn cmp(&self, other: &Self) -> Ordering { + self.0.to_bytes().cmp(&other.0.to_bytes()) + } +} + +impl AssetBase { + /// Deserialize the AssetBase from a byte array. + /// + /// Returns `None` if the byte encoding is invalid or if it corresponds + /// to the identity point. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pallas::Point::from_bytes(bytes) + .and_then(|asset| CtOption::new(AssetBase(asset), !asset.is_identity())) + } + + /// Serialize the AssetBase to its canonical byte representation. + pub fn to_bytes(self) -> [u8; 32] { + self.0.to_bytes() + } + + /// Note type for zatoshi, maintains backward compatibility with Orchard untyped notes. + pub fn zatoshi() -> Self { + AssetBase(pallas::Point::from(zatoshi_asset_base())) + } + + /// The base point used in value commitments. + pub fn cv_base(&self) -> pallas::Point { + self.0 + } + + /// Whether this note represents zatoshi or ZSA asset. + pub fn is_zatoshi(&self) -> Choice { + self.0.ct_eq(&Self::zatoshi().0) + } + + /// Generates a ZSA random asset from a random non-identity Pallas point. + /// + /// Normally, an `AssetBase` is derived from an issuance validating key. For testing purposes, + /// it is sufficient to use a random non-identity Pallas point. This allows generating a random + /// `AssetBase` even when `zsa-issuance` feature is disabled. + /// + /// This is only used in tests. + #[cfg(test)] + pub(crate) fn random(rng: &mut impl CryptoRngCore) -> Self { + loop { + let random_point = pallas::Point::random(&mut *rng); + // Extremely unlikely, but we explicitly reject the identity point. + if bool::from(random_point.is_identity()) { + continue; + } + return Self(random_point); + } + } +} + +impl PartialEq for AssetBase { + fn eq(&self, other: &Self) -> bool { + bool::from(self.0.ct_eq(&other.0)) + } +} diff --git a/src/note/commitment.rs b/src/note/commitment.rs index 0a6001f7a..e3001f71d 100644 --- a/src/note/commitment.rs +++ b/src/note/commitment.rs @@ -8,13 +8,18 @@ use core::iter; +use alloc::vec::Vec; use bitvec::{array::BitArray, order::Lsb0}; use group::ff::{PrimeField, PrimeFieldBits}; use pasta_curves::pallas; -use subtle::{ConstantTimeEq, CtOption}; +use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{ - constants::{fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, L_ORCHARD_BASE}, + constants::{ + fixed_bases::{NOTE_COMMITMENT_PERSONALIZATION, NOTE_ZSA_COMMITMENT_PERSONALIZATION}, + L_ORCHARD_BASE, + }, + note::AssetBase, spec::extract_p, value::NoteValue, }; @@ -30,6 +35,14 @@ impl NoteCommitTrapdoor { pub(crate) fn inner(&self) -> pallas::Scalar { self.0 } + + /// Constructs a `NoteCommitTrapdoor` from the provided scalar value. + /// + /// This constructor is only available in tests. + #[cfg(test)] + pub fn new(trapdoor: pallas::Scalar) -> Self { + Self(trapdoor) + } } /// A commitment to a note. @@ -45,31 +58,59 @@ impl NoteCommitment { } impl NoteCommitment { - /// $NoteCommit^Orchard$. + /// $NoteCommit^{Orchard}$ when the asset is zatoshi, + /// and $NoteCommit^{OrchardZSA}$ otherwise. /// - /// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit]. + /// $NoteCommit^{Orchard}$ is defined in + /// [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit]. + /// $NoteCommit^{OrchardZSA}$ is defined in + /// [ZIP-226: Transfer and Burn of Zcash Shielded Assets][notecommitzsa]. /// /// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit - pub(super) fn derive( + /// [notecommitzsa]: https://zips.z.cash/zip-0226#note-structure-and-commitment + pub(crate) fn derive( g_d: [u8; 32], pk_d: [u8; 32], v: NoteValue, + asset: AssetBase, rho: pallas::Base, psi: pallas::Base, rcm: NoteCommitTrapdoor, ) -> CtOption { - let domain = sinsemilla::CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); - domain - .commit( - iter::empty() - .chain(BitArray::<_, Lsb0>::new(g_d).iter().by_vals()) - .chain(BitArray::<_, Lsb0>::new(pk_d).iter().by_vals()) - .chain(v.to_le_bits().iter().by_vals()) - .chain(rho.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)) - .chain(psi.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)), - &rcm.0, - ) - .map(NoteCommitment) + let common_note_bits = iter::empty() + .chain(BitArray::<_, Lsb0>::new(g_d).iter().by_vals()) + .chain(BitArray::<_, Lsb0>::new(pk_d).iter().by_vals()) + .chain(v.to_le_bits().iter().by_vals()) + .chain(rho.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)) + .chain(psi.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)) + .collect::>(); + + let zec_note_bits = common_note_bits.clone().into_iter(); + + let asset_bits = BitArray::<_, Lsb0>::new(asset.to_bytes()); + let zsa_note_bits = common_note_bits + .into_iter() + .chain(asset_bits.iter().by_vals()); + + // Evaluate ZEC note commitment + let zec_domain = sinsemilla::CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); + let commit_with_zec_domain = zec_domain.commit(zec_note_bits, &rcm.0); + + // Evaluate ZSA note commitment + let zsa_domain = sinsemilla::CommitDomain::new_with_separate_domains( + NOTE_ZSA_COMMITMENT_PERSONALIZATION, + NOTE_COMMITMENT_PERSONALIZATION, + ); + let commit_with_zsa_domain = zsa_domain.commit(zsa_note_bits, &rcm.0); + + // Select the desired commitment in constant-time + let commit = commit_with_zsa_domain.and_then(|zsa_commit| { + commit_with_zec_domain.map(|zec_commit| { + pallas::Point::conditional_select(&zsa_commit, &zec_commit, asset.is_zatoshi()) + }) + }); + + commit.map(NoteCommitment) } } diff --git a/src/note/nullifier.rs b/src/note/nullifier.rs index ab4ef7607..d1f2311f3 100644 --- a/src/note/nullifier.rs +++ b/src/note/nullifier.rs @@ -10,10 +10,11 @@ use group::{ff::PrimeField, Group}; use memuse::DynamicUsage; use pasta_curves::{arithmetic::CurveExt, pallas}; use rand::RngCore; -use subtle::{ConstantTimeEq, CtOption}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::NoteCommitment; use crate::{ + constants::nullifier_l::nullifier_l, keys::NullifierDerivingKey, spec::{extract_p, mod_r_p}, }; @@ -67,17 +68,27 @@ impl Nullifier { /// $DeriveNullifier$. /// /// Defined in [Zcash Protocol Spec § 4.16: Note Commitments and Nullifiers][commitmentsandnullifiers]. + /// A split note's nullifier is offset by $\mathcal{L}^{\mathsf{Orchard}}$, as defined in + /// [ZIP-226: Transfer and Burn of Zcash Shielded Assets][zip226]. /// /// [commitmentsandnullifiers]: https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers - pub(super) fn derive( + /// [zip226]: https://zips.z.cash/zip-0226 + pub(crate) fn derive( nk: &NullifierDerivingKey, rho: pallas::Base, psi: pallas::Base, cm: NoteCommitment, + is_split_note: Choice, ) -> Self { let k = pallas::Point::hash_to_curve("z.cash:Orchard")(b"K"); - Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0))) + let nullifier = k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0; + let split_note_nullifier = nullifier + nullifier_l(); + + let selected_nullifier = + pallas::Point::conditional_select(&nullifier, &split_note_nullifier, is_split_note); + + Nullifier(extract_p(&selected_nullifier)) } } diff --git a/src/pczt.rs b/src/pczt.rs index 60dccc33a..5f2c3e4d4 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -393,7 +393,8 @@ mod tests { use crate::{ builder::{Builder, BundleMetadata, BundleType}, bundle::{BundleVersion, Flags}, - circuit::{OrchardCircuitVersion, ProvingKey, VerifyingKey}, + circuit::{ProvingKey, VerifyingKey}, + circuit_version::OrchardCircuitVersion, constants::MERKLE_DEPTH_ORCHARD, keys::{FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, note::{ExtractedNoteCommitment, NoteVersion, Nullifier, RandomSeed, Rho}, @@ -1515,6 +1516,7 @@ mod tests { pczt_bundle.zkproof = Some(crate::Proof::new(vec![ 0; crate::Proof::expected_proof_size( + pczt_bundle.bundle_version.circuit_version(), pczt_bundle.actions.len() ) ])); diff --git a/src/pczt/prover.rs b/src/pczt/prover.rs index 9821828c2..2eb0942b4 100644 --- a/src/pczt/prover.rs +++ b/src/pczt/prover.rs @@ -34,7 +34,7 @@ impl super::Bundle { /// Also returns an error if required Prover-role fields are missing or invalid, /// or if proof creation fails. /// - /// [`OrchardCircuitVersion::PostNu6_3`]: crate::circuit::OrchardCircuitVersion::PostNu6_3 + /// [`OrchardCircuitVersion::PostNu6_3`]: crate::circuit_version::OrchardCircuitVersion::PostNu6_3 pub fn create_proof( &mut self, pk: &ProvingKey, diff --git a/src/pczt/tx_extractor.rs b/src/pczt/tx_extractor.rs index e71e5e37b..25a1f7183 100644 --- a/src/pczt/tx_extractor.rs +++ b/src/pczt/tx_extractor.rs @@ -59,6 +59,7 @@ impl super::Bundle { // flags is checked when proving or verifying. if let Some(bundle) = &bundle { crate::bundle::validate_proof_size( + bundle.bundle_version().circuit_version(), &bundle.authorization().proof, bundle.actions().len(), )?; diff --git a/src/value.rs b/src/value.rs index 1e52c016b..2f69ffcd0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -54,9 +54,8 @@ use rand::RngCore; use subtle::CtOption; use crate::{ - constants::fixed_bases::{ - VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES, - }, + constants::fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES}, + note::AssetBase, primitives::redpallas::{self, Binding}, }; @@ -356,8 +355,18 @@ impl ValueCommitment { /// [concretehomomorphiccommit]: https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit #[allow(non_snake_case)] pub fn derive(value: ValueSum, rcv: ValueCommitTrapdoor) -> Self { + ValueCommitment::derive_with_asset(value, rcv, AssetBase::zatoshi()) + } + + /// Derives a `ValueCommitment` by $\mathsf{ValueCommit^{OrchardZSA}}$. + /// + /// Defined in [ZIP-226][zip226]. + /// + /// [zip226]: https://zips.z.cash/zip-0226 + #[allow(non_snake_case)] + pub fn derive_with_asset(value: ValueSum, rcv: ValueCommitTrapdoor, asset: AssetBase) -> Self { let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION); - let V = hasher(&VALUE_COMMITMENT_V_BYTES); + let V = asset.cv_base(); let R = hasher(&VALUE_COMMITMENT_R_BYTES); let abs_value = u64::try_from(value.0.abs()).expect("value must be in valid range"); diff --git a/tests/builder.rs b/tests/builder.rs index 65c7611ee..84bc9f2d3 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -4,7 +4,8 @@ use incrementalmerkletree::{Hashable, Marking, Retention}; use orchard::{ builder::{Builder, BundleType}, bundle::{Authorized, BatchValidator, BundleVersion, Flags, TxVersion}, - circuit::{OrchardCircuitVersion, ProvingKey, VerifyingKey}, + circuit::{ProvingKey, VerifyingKey}, + circuit_version::OrchardCircuitVersion, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, note::{ExtractedNoteCommitment, NoteVersion}, note_encryption::{IronwoodDomain, OrchardDomain},