diff --git a/.gitignore b/.gitignore index f4ca20140..18b5267be 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ Cargo.lock .vscode .idea action-circuit-layout.png +*.[0-9] +*.[0-9][0-9] diff --git a/Cargo.toml b/Cargo.toml index 74dae4945..bf1ee7e70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,12 @@ rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] aes = "0.8" bitvec = "1" blake2b_simd = "=1.0.1" # Last version required rust 1.66 +half = "=2.2.1" # Last version requires Rust 1.70 ff = "0.13" fpe = "0.6" group = { version = "0.13", features = ["wnaf-memuse"] } -halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "zsa1" } -halo2_proofs = { git = "https://github.com/QED-it/halo2", branch = "zsa1", default-features = false, features = ["batch", "floor-planner-v1-legacy-pdqsort"] } +halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "orchardzsa-backward-compatability" } +halo2_proofs = { git = "https://github.com/QED-it/halo2", branch = "orchardzsa-backward-compatability", default-features = false, features = ["batch", "floor-planner-v1-legacy-pdqsort"] } hex = "0.4" k256 = { version = "0.13.0", features = ["arithmetic", "schnorr"] } lazy_static = "1" @@ -54,14 +55,12 @@ plotters = { version = "0.3.0", optional = true } [dev-dependencies] bridgetree = "0.4" -criterion = "0.4" #Pinned: 0.5 depends on clap 4 which has MSRV 1.70 -halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "zsa1", features = ["test-dependencies"] } -hex = "0.4" +criterion = "0.4" # 0.5 depends on clap 4 which has MSRV 1.70 +halo2_gadgets = { git = "https://github.com/QED-it/halo2", branch = "orchardzsa-backward-compatability", features = ["test-dependencies"] } proptest = "1.0.0" zcash_note_encryption_zsa = { package = "zcash_note_encryption", version = "0.4", git = "https://github.com/QED-it/zcash_note_encryption", branch = "zsa1", features = ["pre-zip-212"] } incrementalmerkletree = { version = "0.5", features = ["test-dependencies"] } ahash = "=0.8.6" #Pinned: 0.8.7 depends on Rust 1.72 -half = "=2.2.1" #Pinned: 2.3.1 requires Rust 1.70 [target.'cfg(unix)'.dev-dependencies] inferno = "0.11" #Pinned diff --git a/benches/circuit.rs b/benches/circuit.rs index f26cc3507..b773fd02e 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -12,6 +12,7 @@ use orchard::{ bundle::Flags, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, Scope, SpendingKey}, + orchard_flavors::OrchardZSA, value::NoteValue, Anchor, Bundle, }; @@ -23,8 +24,9 @@ fn criterion_benchmark(c: &mut Criterion) { let sk = SpendingKey::from_bytes([7; 32]).unwrap(); let recipient = FullViewingKey::from(&sk).address_at(0u32, Scope::External); - let vk = VerifyingKey::build(); - let pk = ProvingKey::build(); + // FIXME: consider adding test for OrchardVanilla as well + let vk = VerifyingKey::build::(); + let pk = ProvingKey::build::(); let create_bundle = |num_recipients| { let mut builder = Builder::new( @@ -42,7 +44,7 @@ fn criterion_benchmark(c: &mut Criterion) { ) .unwrap(); } - let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); + let bundle: Bundle<_, i64, OrchardZSA> = builder.build(rng).unwrap(); let instances: Vec<_> = bundle .actions() diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index ba0f62112..528d201ac 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -5,7 +5,8 @@ use orchard::{ circuit::ProvingKey, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey}, note::AssetBase, - note_encryption_v3::{CompactAction, OrchardDomainV3}, + note_encryption::{action::CompactAction, OrchardDomainBase}, + orchard_flavors::OrchardZSA, value::NoteValue, Anchor, Bundle, }; @@ -15,9 +16,12 @@ use zcash_note_encryption_zsa::{batch, try_compact_note_decryption, try_note_dec #[cfg(unix)] use pprof::criterion::{Output, PProfProfiler}; +type OrchardDomainZSA = OrchardDomainBase; + fn bench_note_decryption(c: &mut Criterion) { let rng = OsRng; - let pk = ProvingKey::build(); + // FIXME: consider adding test for OrchardVanilla as well + let pk = ProvingKey::build::(); let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap()); let valid_ivk = fvk.to_ivk(Scope::External); @@ -70,7 +74,7 @@ fn bench_note_decryption(c: &mut Criterion) { None, ) .unwrap(); - let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); + let bundle: Bundle<_, i64, OrchardZSA> = builder.build(rng).unwrap(); bundle .create_proof(&pk, rng) .unwrap() @@ -79,7 +83,7 @@ fn bench_note_decryption(c: &mut Criterion) { }; let action = bundle.actions().first(); - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainZSA::for_action(action); let compact = { let mut group = c.benchmark_group("note-decryption"); @@ -120,12 +124,12 @@ fn bench_note_decryption(c: &mut Criterion) { let ivks = 2; let valid_ivks = vec![valid_ivk; ivks]; let actions: Vec<_> = (0..100) - .map(|_| (OrchardDomainV3::for_action(action), action.clone())) + .map(|_| (OrchardDomainZSA::for_action(action), action.clone())) .collect(); let compact: Vec<_> = (0..100) .map(|_| { ( - OrchardDomainV3::for_action(action), + OrchardDomainZSA::for_action(action), CompactAction::from(action), ) }) diff --git a/src/action.rs b/src/action.rs index 58b273f17..eb9380c50 100644 --- a/src/action.rs +++ b/src/action.rs @@ -2,6 +2,7 @@ use memuse::DynamicUsage; use crate::{ note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext}, + note_encryption::OrchardDomain, primitives::redpallas::{self, SpendAuth}, value::ValueCommitment, }; @@ -15,7 +16,7 @@ use crate::{ /// Internally, this may both consume a note and create a note, or it may do only one of /// the two. TODO: Determine which is more efficient (circuit size vs bundle size). #[derive(Debug, Clone)] -pub struct Action { +pub struct Action { /// The nullifier of the note being spent. nf: Nullifier, /// The randomized verification key for the note being spent. @@ -23,22 +24,22 @@ pub struct Action { /// A commitment to the new note being created. cmx: ExtractedNoteCommitment, /// The transmitted note ciphertext. - encrypted_note: TransmittedNoteCiphertext, + encrypted_note: TransmittedNoteCiphertext, /// A commitment to the net value created or consumed by this action. cv_net: ValueCommitment, /// The authorization for this action. authorization: A, } -impl Action { +impl Action { /// Constructs an `Action` from its constituent parts. pub fn from_parts( nf: Nullifier, rk: redpallas::VerificationKey, cmx: ExtractedNoteCommitment, - encrypted_note: TransmittedNoteCiphertext, + encrypted_note: TransmittedNoteCiphertext, cv_net: ValueCommitment, - authorization: T, + authorization: A, ) -> Self { Action { nf, @@ -66,7 +67,7 @@ impl Action { } /// Returns the encrypted note ciphertext. - pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext { + pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext { &self.encrypted_note } @@ -76,12 +77,12 @@ impl Action { } /// Returns the authorization for this action. - pub fn authorization(&self) -> &T { + pub fn authorization(&self) -> &A { &self.authorization } /// Transitions this action from one authorization state to another. - pub fn map(self, step: impl FnOnce(T) -> U) -> Action { + pub fn map(self, step: impl FnOnce(A) -> U) -> Action { Action { nf: self.nf, rk: self.rk, @@ -93,7 +94,7 @@ impl Action { } /// Transitions this action from one authorization state to another. - pub fn try_map(self, step: impl FnOnce(T) -> Result) -> Result, E> { + pub fn try_map(self, step: impl FnOnce(A) -> Result) -> Result, E> { Ok(Action { nf: self.nf, rk: self.rk, @@ -105,7 +106,7 @@ impl Action { } } -impl DynamicUsage for Action> { +impl DynamicUsage for Action, D> { #[inline(always)] fn dynamic_usage(&self) -> usize { 0 @@ -132,6 +133,7 @@ pub(crate) mod testing { commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier, testing::arb_note, TransmittedNoteCiphertext, }, + note_encryption::OrchardDomain, primitives::redpallas::{ self, testing::{arb_spendauth_signing_key, arb_spendauth_verification_key}, @@ -141,70 +143,82 @@ pub(crate) mod testing { use super::Action; - prop_compose! { - /// Generate an action without authorization data. - pub fn arb_unauthorized_action(spend_value: NoteValue, output_value: NoteValue)( - nf in arb_nullifier(), - rk in arb_spendauth_verification_key(), - note in arb_note(output_value), - asset in arb_asset_base() - ) -> Action<()> { - let cmx = ExtractedNoteCommitment::from(note.commitment()); - let cv_net = ValueCommitment::derive( - spend_value - output_value, - ValueCommitTrapdoor::zero(), - asset - ); - // FIXME: make a real one from the note. - let encrypted_note = TransmittedNoteCiphertext { - epk_bytes: [0u8; 32], - enc_ciphertext: [0u8; 612], - out_ciphertext: [0u8; 80] - }; - Action { - nf, - rk, - cmx, - encrypted_note, - cv_net, - authorization: () + /// `ActionArb` serves as a utility structure in property-based testing, designed specifically to adapt + /// `arb_...` functions for compatibility with both variations of the Orchard protocol: Vanilla and ZSA. + /// This adaptation is necessary due to the proptest crate's limitation, which prevents the direct + /// transformation of `arb_...` functions into generic forms suitable for testing different protocol + /// flavors. + #[derive(Debug)] + pub struct ActionArb { + phantom: std::marker::PhantomData, + } + + impl ActionArb { + prop_compose! { + /// Generate an action without authorization data. + pub fn arb_unauthorized_action(spend_value: NoteValue, output_value: NoteValue)( + nf in arb_nullifier(), + rk in arb_spendauth_verification_key(), + note in arb_note(output_value), + asset in arb_asset_base() + ) -> Action<(), D> { + let cmx = ExtractedNoteCommitment::from(note.commitment()); + let cv_net = ValueCommitment::derive( + spend_value - output_value, + ValueCommitTrapdoor::zero(), + asset + ); + // FIXME: make a real one from the note. + let encrypted_note = TransmittedNoteCiphertext:: { + epk_bytes: [0u8; 32], + enc_ciphertext: D::NoteCiphertextBytes::from(vec![0u8; D::ENC_CIPHERTEXT_SIZE].as_ref()), + out_ciphertext: [0u8; 80] + }; + Action { + nf, + rk, + cmx, + encrypted_note, + cv_net, + authorization: () + } } } - } - prop_compose! { - /// Generate an action with invalid (random) authorization data. - pub fn arb_action(spend_value: NoteValue, output_value: NoteValue)( - nf in arb_nullifier(), - sk in arb_spendauth_signing_key(), - note in arb_note(output_value), - rng_seed in prop::array::uniform32(prop::num::u8::ANY), - fake_sighash in prop::array::uniform32(prop::num::u8::ANY), - asset in arb_asset_base() - ) -> Action> { - let cmx = ExtractedNoteCommitment::from(note.commitment()); - let cv_net = ValueCommitment::derive( - spend_value - output_value, - ValueCommitTrapdoor::zero(), - asset - ); - - // FIXME: make a real one from the note. - let encrypted_note = TransmittedNoteCiphertext { - epk_bytes: [0u8; 32], - enc_ciphertext: [0u8; 612], - out_ciphertext: [0u8; 80] - }; - - let rng = StdRng::from_seed(rng_seed); - - Action { - nf, - rk: redpallas::VerificationKey::from(&sk), - cmx, - encrypted_note, - cv_net, - authorization: sk.sign(rng, &fake_sighash), + prop_compose! { + /// Generate an action with invalid (random) authorization data. + pub fn arb_action(spend_value: NoteValue, output_value: NoteValue)( + nf in arb_nullifier(), + sk in arb_spendauth_signing_key(), + note in arb_note(output_value), + rng_seed in prop::array::uniform32(prop::num::u8::ANY), + fake_sighash in prop::array::uniform32(prop::num::u8::ANY), + asset in arb_asset_base() + ) -> Action, D> { + let cmx = ExtractedNoteCommitment::from(note.commitment()); + let cv_net = ValueCommitment::derive( + spend_value - output_value, + ValueCommitTrapdoor::zero(), + asset + ); + + // FIXME: make a real one from the note. + let encrypted_note = TransmittedNoteCiphertext:: { + epk_bytes: [0u8; 32], + enc_ciphertext: D::NoteCiphertextBytes::from(vec![0u8; D::ENC_CIPHERTEXT_SIZE].as_ref()), + out_ciphertext: [0u8; 80] + }; + + let rng = StdRng::from_seed(rng_seed); + + Action { + nf, + rk: redpallas::VerificationKey::from(&sk), + cmx, + encrypted_note, + cv_net, + authorization: sk.sign(rng, &fake_sighash), + } } } } diff --git a/src/builder.rs b/src/builder.rs index 5e51162e5..c94ae510f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -10,18 +10,19 @@ use nonempty::NonEmpty; use pasta_curves::pallas; use rand::{prelude::SliceRandom, CryptoRng, RngCore}; -use crate::note::AssetBase; +use zcash_note_encryption_zsa::NoteEncryption; + use crate::{ action::Action, address::Address, - bundle::{Authorization, Authorized, Bundle, Flags}, - circuit::{Circuit, Instance, Proof, ProvingKey}, + bundle::{Authorization, Authorized, Bundle, Flags, OrchardHash}, + circuit::{CircuitBase, Instance, OrchardCircuit, Proof, ProvingKey}, keys::{ FullViewingKey, OutgoingViewingKey, Scope, SpendAuthorizingKey, SpendValidatingKey, SpendingKey, }, - note::{Note, TransmittedNoteCiphertext}, - note_encryption_v3::OrchardNoteEncryption, + note::{AssetBase, Note, TransmittedNoteCiphertext}, + note_encryption::{OrchardDomain, OrchardDomainBase}, primitives::redpallas::{self, Binding, SpendAuth}, tree::{Anchor, MerklePath}, value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum}, @@ -252,7 +253,10 @@ impl ActionInfo { /// # Panics /// /// Panics if the asset types of the spent and output notes do not match. - fn build(self, mut rng: impl RngCore) -> (Action, Circuit) { + fn build( + self, + mut rng: impl RngCore, + ) -> (Action, CircuitBase) { assert_eq!( self.spend.note.asset(), self.output.asset, @@ -278,7 +282,7 @@ impl ActionInfo { let cm_new = note.commitment(); let cmx = cm_new.into(); - let encryptor = OrchardNoteEncryption::new( + let encryptor = NoteEncryption::>::new( self.output.ovk, note, self.output.memo.unwrap_or_else(|| { @@ -290,7 +294,7 @@ impl ActionInfo { let encrypted_note = TransmittedNoteCiphertext { epk_bytes: encryptor.epk().to_bytes().0, - enc_ciphertext: encryptor.encrypt_note_plaintext().0, + enc_ciphertext: encryptor.encrypt_note_plaintext(), out_ciphertext: encryptor.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut rng), }; @@ -306,7 +310,7 @@ impl ActionInfo { parts: SigningParts { ak, alpha }, }, ), - Circuit::from_action_context_unchecked(self.spend, note, alpha, self.rcv), + CircuitBase::::from_action_context_unchecked(self.spend, note, alpha, self.rcv), ) } } @@ -322,6 +326,8 @@ pub struct Builder { anchor: Anchor, } +type UnauthorizedBundle = Bundle, Unauthorized>, V, D>; + impl Builder { /// Constructs a new empty builder for an Orchard bundle. pub fn new(flags: Flags, anchor: Anchor) -> Self { @@ -344,7 +350,7 @@ impl Builder { /// Returns an error if the given Merkle path does not have the required anchor for /// the given note. /// - /// [`OrchardDomain`]: crate::note_encryption_v3::OrchardDomainV3 + /// [`OrchardDomain`]: crate::note_encryption::OrchardDomain /// [`MerkleHashOrchard`]: crate::tree::MerkleHashOrchard pub fn add_spend( &mut self, @@ -452,7 +458,7 @@ impl Builder { .iter() .map(|recipient| NoteValue::zero() - recipient.value), ) - .fold(Some(ValueSum::zero()), |acc, note_value| acc? + note_value) + .try_fold(ValueSum::zero(), |acc, note_value| acc + note_value) .ok_or(OverflowError)?; i64::try_from(value_balance).and_then(|i| V::try_from(i).map_err(|_| value::OverflowError)) } @@ -475,10 +481,13 @@ impl Builder { /// /// The returned bundle will have no proof or signatures; these can be applied with /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. - pub fn build + Copy + Into>( + pub fn build< + V: TryFrom + Copy + Into, + D: OrchardDomain + OrchardCircuit + OrchardHash, + >( self, mut rng: impl RngCore, - ) -> Result, V>, BuildError> { + ) -> Result, BuildError> { let mut pre_actions: Vec<_> = Vec::new(); // Pair up the spends and recipients, extending with dummy values as necessary. @@ -529,9 +538,7 @@ impl Builder { let native_value_balance: V = pre_actions .iter() .filter(|action| action.spend.note.asset().is_native().into()) - .fold(Some(ValueSum::zero()), |acc, action| { - acc? + action.value_sum() - }) + .try_fold(ValueSum::zero(), |acc, action| acc + action.value_sum()) .ok_or(OverflowError)? .into()?; @@ -543,8 +550,10 @@ impl Builder { .into_bsk(); // Create the actions. - let (actions, circuits): (Vec<_>, Vec<_>) = - pre_actions.into_iter().map(|a| a.build(&mut rng)).unzip(); + let (actions, circuits): (Vec<_>, Vec<_>) = pre_actions + .into_iter() + .map(|a| a.build::(&mut rng)) + .unzip(); let bundle = Bundle::from_parts( NonEmpty::from_vec(actions).unwrap(), @@ -635,11 +644,11 @@ impl Authorization for InProgress /// /// This struct contains the private data needed to create a [`Proof`] for a [`Bundle`]. #[derive(Clone, Debug)] -pub struct Unproven { - circuits: Vec, +pub struct Unproven { + circuits: Vec>, } -impl InProgress { +impl InProgress, S> { /// Creates the proof for this bundle. pub fn create_proof( &self, @@ -651,13 +660,15 @@ impl InProgress { } } -impl Bundle, V> { +impl + Bundle, S>, V, D> +{ /// Creates the proof for this bundle. pub fn create_proof( self, pk: &ProvingKey, mut rng: impl RngCore, - ) -> Result, V>, BuildError> { + ) -> Result, V, D>, BuildError> { let instances: Vec<_> = self .actions() .iter() @@ -740,7 +751,7 @@ impl MaybeSigned { } } -impl Bundle, V> { +impl Bundle, V, D> { /// Loads the sighash into this bundle, preparing it for signing. /// /// This API ensures that all signatures are created over the same sighash. @@ -748,7 +759,7 @@ impl Bundle, V> { self, mut rng: R, sighash: [u8; 32], - ) -> Bundle, V> { + ) -> Bundle, V, D> { self.map_authorization( &mut rng, |rng, _, SigningMetadata { dummy_ask, parts }| { @@ -769,7 +780,7 @@ impl Bundle, V> { } } -impl Bundle, V> { +impl Bundle, V, D> { /// Applies signatures to this bundle, in order to authorize it. /// /// This is a helper method that wraps [`Bundle::prepare`], [`Bundle::sign`], and @@ -779,7 +790,7 @@ impl Bundle, V> { mut rng: R, sighash: [u8; 32], signing_keys: &[SpendAuthorizingKey], - ) -> Result, BuildError> { + ) -> Result, BuildError> { signing_keys .iter() .fold(self.prepare(&mut rng, sighash), |partial, ask| { @@ -789,7 +800,7 @@ impl Bundle, V> { } } -impl Bundle, V> { +impl Bundle, V, D> { /// Signs this bundle with the given [`SpendAuthorizingKey`]. /// /// This will apply signatures for all notes controlled by this spending key. @@ -852,11 +863,11 @@ impl Bundle, V> { } } -impl Bundle, V> { +impl Bundle, V, D> { /// Finalizes this bundle, enabling it to be included in a transaction. /// /// Returns an error if any signatures are missing. - pub fn finalize(self) -> Result, BuildError> { + pub fn finalize(self) -> Result, BuildError> { self.try_map_authorization( &mut (), |_, _, maybe| maybe.finalize(), @@ -917,10 +928,11 @@ pub mod testing { use crate::note::AssetBase; use crate::{ address::testing::arb_address, - bundle::{Authorized, Bundle, Flags}, - circuit::ProvingKey, + bundle::{Authorized, Bundle, Flags, OrchardHash}, + circuit::{OrchardCircuit, ProvingKey}, keys::{testing::arb_spending_key, FullViewingKey, SpendAuthorizingKey, SpendingKey}, note::testing::arb_note, + note_encryption::OrchardDomain, tree::{Anchor, MerkleHashOrchard, MerklePath}, value::{testing::arb_positive_note_value, NoteValue, MAX_NOTE_VALUE}, Address, Note, @@ -947,7 +959,12 @@ pub mod testing { impl ArbitraryBundleInputs { /// Create a bundle from the set of arbitrary bundle inputs. - fn into_bundle + Copy + Into>(mut self) -> Bundle { + fn into_bundle< + V: TryFrom + Copy + Into, + D: OrchardDomain + OrchardCircuit + OrchardHash, + >( + mut self, + ) -> Bundle { let fvk = FullViewingKey::from(&self.sk); let flags = Flags::from_parts(true, true, true); let mut builder = Builder::new(flags, self.anchor); @@ -965,7 +982,7 @@ pub mod testing { .unwrap(); } - let pk = ProvingKey::build(); + let pk = ProvingKey::build::(); builder .build(&mut self.rng) .unwrap() @@ -978,70 +995,82 @@ pub mod testing { } } - prop_compose! { - /// Produce a random valid Orchard bundle. - fn arb_bundle_inputs(sk: SpendingKey) - ( - n_notes in 1usize..30, - n_recipients in 1..30, + impl BuilderArb { + prop_compose! { + /// Produce a random valid Orchard bundle. + fn arb_bundle_inputs(sk: SpendingKey) + ( + n_notes in 1usize..30, + n_recipients in 1..30, - ) - ( - // generate note values that we're certain won't exceed MAX_NOTE_VALUE in total - notes in vec( - arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note), - n_notes - ), - recipient_amounts in vec( - arb_address().prop_flat_map(move |a| { - arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64) - .prop_map(move |v| { - (a,v, AssetBase::native()) - }) - }), - n_recipients as usize, - ), - rng_seed in prop::array::uniform32(prop::num::u8::ANY) - ) -> ArbitraryBundleInputs { - use crate::constants::MERKLE_DEPTH_ORCHARD; - let mut frontier = Frontier::::empty(); - let mut notes_and_auth_paths: Vec<(Note, MerklePath)> = Vec::new(); - - for note in notes.iter() { - let leaf = MerkleHashOrchard::from_cmx(¬e.commitment().into()); - frontier.append(leaf); - - let path = frontier - .witness(|addr| Some(::empty_root(addr.level()))) - .ok() - .flatten() - .expect("we can always construct a correct Merkle path"); - notes_and_auth_paths.push((*note, path.into())); - } + ) + ( + // generate note values that we're certain won't exceed MAX_NOTE_VALUE in total + notes in vec( + arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note), + n_notes + ), + recipient_amounts in vec( + arb_address().prop_flat_map(move |a| { + arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64) + .prop_map(move |v| { + (a,v, AssetBase::native()) + }) + }), + n_recipients as usize, + ), + rng_seed in prop::array::uniform32(prop::num::u8::ANY) + ) -> ArbitraryBundleInputs { + use crate::constants::MERKLE_DEPTH_ORCHARD; + let mut frontier = Frontier::::empty(); + let mut notes_and_auth_paths: Vec<(Note, MerklePath)> = Vec::new(); + + for note in notes.iter() { + let leaf = MerkleHashOrchard::from_cmx(¬e.commitment().into()); + frontier.append(leaf); + + let path = frontier + .witness(|addr| Some(::empty_root(addr.level()))) + .ok() + .flatten() + .expect("we can always construct a correct Merkle path"); + notes_and_auth_paths.push((*note, path.into())); + } - ArbitraryBundleInputs { - rng: StdRng::from_seed(rng_seed), - sk, - anchor: frontier.root().into(), - notes: notes_and_auth_paths, - recipient_amounts + ArbitraryBundleInputs { + rng: StdRng::from_seed(rng_seed), + sk, + anchor: frontier.root().into(), + notes: notes_and_auth_paths, + recipient_amounts + } } } - } - /// Produce an arbitrary valid Orchard bundle using a random spending key. - pub fn arb_bundle + Debug + Copy + Into>( - ) -> impl Strategy> { - arb_spending_key() - .prop_flat_map(arb_bundle_inputs) - .prop_map(|inputs| inputs.into_bundle::()) + /// Produce an arbitrary valid Orchard bundle using a random spending key. + pub fn arb_bundle + Debug + Copy + Into>( + ) -> impl Strategy> { + arb_spending_key() + .prop_flat_map(BuilderArb::::arb_bundle_inputs) + .prop_map(|inputs| inputs.into_bundle::()) + } + + /// Produce an arbitrary valid Orchard bundle using a specified spending key. + pub fn arb_bundle_with_key + Debug + Copy + Into>( + k: SpendingKey, + ) -> impl Strategy> { + BuilderArb::::arb_bundle_inputs(k).prop_map(|inputs| inputs.into_bundle::()) + } } - /// Produce an arbitrary valid Orchard bundle using a specified spending key. - pub fn arb_bundle_with_key + Debug + Copy + Into>( - k: SpendingKey, - ) -> impl Strategy> { - arb_bundle_inputs(k).prop_map(|inputs| inputs.into_bundle::()) + /// `BuilderArb` serves as a utility structure in property-based testing, designed specifically to adapt + /// `arb_...` functions for compatibility with both variations of the Orchard protocol: Vanilla and ZSA. + /// This adaptation is necessary due to the proptest crate's limitation, which prevents the direct + /// transformation of `arb_...` functions into generic forms suitable for testing different protocol + /// flavors. + #[derive(Debug)] + pub struct BuilderArb { + phantom: std::marker::PhantomData, } } @@ -1049,20 +1078,23 @@ pub mod testing { mod tests { use rand::rngs::OsRng; - use super::Builder; - use crate::note::AssetBase; use crate::{ bundle::{Authorized, Bundle, Flags}, circuit::ProvingKey, constants::MERKLE_DEPTH_ORCHARD, keys::{FullViewingKey, Scope, SpendingKey}, + note::AssetBase, + orchard_flavors::OrchardZSA, tree::EMPTY_ROOTS, value::NoteValue, }; + use super::Builder; + #[test] fn shielding_bundle() { - let pk = ProvingKey::build(); + // FIXME: consider adding test for OrchardVanilla as well + let pk = ProvingKey::build::(); let mut rng = OsRng; let sk = SpendingKey::random(&mut rng); @@ -1086,7 +1118,7 @@ mod tests { let balance: i64 = builder.value_balance().unwrap(); assert_eq!(balance, -5000); - let bundle: Bundle = builder + let bundle: Bundle = builder .build(&mut rng) .unwrap() .create_proof(&pk, &mut rng) diff --git a/src/bundle.rs b/src/bundle.rs index 60cd3d75f..b3793b9bd 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -13,21 +13,22 @@ use memuse::DynamicUsage; use nonempty::NonEmpty; use zcash_note_encryption_zsa::{try_note_decryption, try_output_recovery_with_ovk}; -use crate::note::AssetBase; use crate::{ action::Action, address::Address, bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data}, circuit::{Instance, Proof, VerifyingKey}, keys::{IncomingViewingKey, OutgoingViewingKey, PreparedIncomingViewingKey}, - note::Note, - note_encryption_v3::OrchardDomainV3, + note::{AssetBase, Note}, + note_encryption::{OrchardDomain, OrchardDomainBase}, primitives::redpallas::{self, Binding, SpendAuth}, tree::Anchor, value::{ValueCommitTrapdoor, ValueCommitment, ValueSum}, }; -impl Action { +pub use commitments::OrchardHash; + +impl Action { /// Prepares the public instance for this action, for creating and verifying the /// bundle proof. pub fn to_instance(&self, flags: Flags, anchor: Anchor) -> Instance { @@ -152,9 +153,9 @@ pub trait Authorization: fmt::Debug { /// A bundle of actions to be applied to the ledger. #[derive(Clone)] -pub struct Bundle { +pub struct Bundle { /// The list of actions that make up this bundle. - actions: NonEmpty>, + actions: NonEmpty>, /// Orchard-specific transaction-level flags for this bundle. flags: Flags, /// The net value moved out of the Orchard shielded pool. @@ -162,18 +163,19 @@ pub struct Bundle { /// This is the sum of Orchard spends minus the sum of Orchard outputs. value_balance: V, /// Assets intended for burning + // FIXME: use BurnType like it's in Zebra? Put it as another param of Domain trait burn: Vec<(AssetBase, V)>, /// The root of the Orchard commitment tree that this bundle commits to. anchor: Anchor, /// The authorization for this bundle. - authorization: T, + authorization: A, } -impl fmt::Debug for Bundle { +impl fmt::Debug for Bundle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// Helper struct for debug-printing actions without exposing `NonEmpty`. - struct Actions<'a, T>(&'a NonEmpty>); - impl<'a, T: fmt::Debug> fmt::Debug for Actions<'a, T> { + struct Actions<'a, A, D: OrchardDomain>(&'a NonEmpty>); + impl<'a, A: fmt::Debug, D: OrchardDomain> fmt::Debug for Actions<'a, A, D> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.0.iter()).finish() } @@ -189,15 +191,15 @@ impl fmt::Debug for Bundle { } } -impl Bundle { +impl Bundle { /// Constructs a `Bundle` from its constituent parts. pub fn from_parts( - actions: NonEmpty>, + actions: NonEmpty>, flags: Flags, value_balance: V, burn: Vec<(AssetBase, V)>, anchor: Anchor, - authorization: T, + authorization: A, ) -> Self { Bundle { actions, @@ -210,7 +212,7 @@ impl Bundle { } /// Returns the list of actions that make up this bundle. - pub fn actions(&self) -> &NonEmpty> { + pub fn actions(&self) -> &NonEmpty> { &self.actions } @@ -239,7 +241,7 @@ impl Bundle { /// Returns the authorization for this bundle. /// /// In the case of a `Bundle`, this is the proof and binding signature. - pub fn authorization(&self) -> &T { + pub fn authorization(&self) -> &A { &self.authorization } @@ -248,7 +250,7 @@ impl Bundle { pub fn try_map_value_balance Result>( self, f: F, - ) -> Result, E> { + ) -> Result, E> { Ok(Bundle { actions: self.actions, flags: self.flags, @@ -267,9 +269,9 @@ impl Bundle { pub fn map_authorization( self, context: &mut R, - mut spend_auth: impl FnMut(&mut R, &T, T::SpendAuth) -> U::SpendAuth, - step: impl FnOnce(&mut R, T) -> U, - ) -> Bundle { + mut spend_auth: impl FnMut(&mut R, &A, A::SpendAuth) -> U::SpendAuth, + step: impl FnOnce(&mut R, A) -> U, + ) -> Bundle { let authorization = self.authorization; Bundle { actions: self @@ -287,9 +289,9 @@ impl Bundle { pub fn try_map_authorization( self, context: &mut R, - mut spend_auth: impl FnMut(&mut R, &T, T::SpendAuth) -> Result, - step: impl FnOnce(&mut R, T) -> Result, - ) -> Result, E> { + mut spend_auth: impl FnMut(&mut R, &A, A::SpendAuth) -> Result, + step: impl FnOnce(&mut R, A) -> Result, + ) -> Result, E> { let authorization = self.authorization; let new_actions = self .actions @@ -330,7 +332,7 @@ impl Bundle { .iter() .enumerate() .filter_map(|(idx, action)| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainBase::::for_action(action); prepared_keys.iter().find_map(|(ivk, prepared_ivk)| { try_note_decryption(&domain, prepared_ivk, action) .map(|(n, a, m)| (idx, (*ivk).clone(), n, a, m)) @@ -349,7 +351,7 @@ impl Bundle { ) -> Option<(Note, Address, [u8; 512])> { let prepared_ivk = PreparedIncomingViewingKey::new(key); self.actions.get(action_idx).and_then(move |action| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainBase::::for_action(action); try_note_decryption(&domain, &prepared_ivk, action) }) } @@ -366,7 +368,7 @@ impl Bundle { .iter() .enumerate() .filter_map(|(idx, action)| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainBase::::for_action(action); keys.iter().find_map(move |key| { try_output_recovery_with_ovk( &domain, @@ -390,7 +392,7 @@ impl Bundle { key: &OutgoingViewingKey, ) -> Option<(Note, Address, [u8; 512])> { self.actions.get(action_idx).and_then(move |action| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainBase::::for_action(action); try_output_recovery_with_ovk( &domain, key, @@ -402,7 +404,7 @@ impl Bundle { } } -impl> Bundle { +impl, D: OrchardDomain + OrchardHash> Bundle { /// Computes a commitment to the effects of this bundle, suitable for inclusion within /// a transaction ID. pub fn commitment(&self) -> BundleCommitment { @@ -471,7 +473,7 @@ impl Authorized { } } -impl Bundle { +impl Bundle { /// Computes a commitment to the authorizing data within for this bundle. /// /// This together with `Bundle::commitment` bind the entire bundle. @@ -487,7 +489,7 @@ impl Bundle { } } -impl DynamicUsage for Bundle { +impl DynamicUsage for Bundle { fn dynamic_usage(&self) -> usize { self.actions.dynamic_usage() + self.value_balance.dynamic_usage() @@ -552,9 +554,10 @@ pub mod testing { use super::{Action, Authorization, Authorized, Bundle, Flags}; - pub use crate::action::testing::{arb_action, arb_unauthorized_action}; + pub use crate::action::testing::ActionArb; use crate::note::asset_base::testing::arb_zsa_asset_base; use crate::note::AssetBase; + use crate::note_encryption::OrchardDomain; use crate::value::testing::arb_value_sum; /// Marker for an unauthorized bundle with no proofs or signatures. @@ -565,142 +568,154 @@ pub mod testing { type SpendAuth = (); } - /// Generate an unauthorized action having spend and output values less than MAX_NOTE_VALUE / n_actions. - pub fn arb_unauthorized_action_n( - n_actions: usize, - flags: Flags, - ) -> impl Strategy)> { - let spend_value_gen = if flags.spends_enabled { - Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) - } else { - Strategy::boxed(Just(NoteValue::zero())) - }; + /// `BuilderArb` serves as a utility structure in property-based testing, designed specifically to adapt + /// `arb_...` functions for compatibility with both variations of the Orchard protocol: Vanilla and ZSA. + /// This adaptation is necessary due to the proptest crate's limitation, which prevents the direct + /// transformation of `arb_...` functions into generic forms suitable for testing different protocol + /// flavors. + #[derive(Debug)] + pub struct BundleArb { + phantom: std::marker::PhantomData, + } - spend_value_gen.prop_flat_map(move |spend_value| { - let output_value_gen = if flags.outputs_enabled { + impl BundleArb { + /// Generate an unauthorized action having spend and output values less than MAX_NOTE_VALUE / n_actions. + pub fn arb_unauthorized_action_n( + n_actions: usize, + flags: Flags, + ) -> impl Strategy)> { + let spend_value_gen = if flags.spends_enabled { Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) } else { Strategy::boxed(Just(NoteValue::zero())) }; - output_value_gen.prop_flat_map(move |output_value| { - arb_unauthorized_action(spend_value, output_value) - .prop_map(move |a| (spend_value - output_value, a)) - }) - }) - } + spend_value_gen.prop_flat_map(move |spend_value| { + let output_value_gen = if flags.outputs_enabled { + Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) + } else { + Strategy::boxed(Just(NoteValue::zero())) + }; - /// Generate an authorized action having spend and output values less than MAX_NOTE_VALUE / n_actions. - pub fn arb_action_n( - n_actions: usize, - flags: Flags, - ) -> impl Strategy>)> { - let spend_value_gen = if flags.spends_enabled { - Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) - } else { - Strategy::boxed(Just(NoteValue::zero())) - }; + output_value_gen.prop_flat_map(move |output_value| { + ActionArb::arb_unauthorized_action(spend_value, output_value) + .prop_map(move |a| (spend_value - output_value, a)) + }) + }) + } - spend_value_gen.prop_flat_map(move |spend_value| { - let output_value_gen = if flags.outputs_enabled { + /// Generate an authorized action having spend and output values less than MAX_NOTE_VALUE / n_actions. + pub fn arb_action_n( + n_actions: usize, + flags: Flags, + ) -> impl Strategy, D>)> { + let spend_value_gen = if flags.spends_enabled { Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) } else { Strategy::boxed(Just(NoteValue::zero())) }; - output_value_gen.prop_flat_map(move |output_value| { - arb_action(spend_value, output_value) - .prop_map(move |a| (spend_value - output_value, a)) + spend_value_gen.prop_flat_map(move |spend_value| { + let output_value_gen = if flags.outputs_enabled { + Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) + } else { + Strategy::boxed(Just(NoteValue::zero())) + }; + + output_value_gen.prop_flat_map(move |output_value| { + ActionArb::arb_action(spend_value, output_value) + .prop_map(move |a| (spend_value - output_value, a)) + }) }) - }) - } + } - prop_compose! { - /// Create an arbitrary vector of assets to burn. - pub fn arb_asset_to_burn() - ( - asset_base in arb_zsa_asset_base(), - value in arb_value_sum() - ) -> (AssetBase, ValueSum) { - (asset_base, value) + prop_compose! { + /// Create an arbitrary vector of assets to burn. + pub fn arb_asset_to_burn() + ( + asset_base in arb_zsa_asset_base(), + value in arb_value_sum() + ) -> (AssetBase, ValueSum) { + (asset_base, value) + } } - } - prop_compose! { - /// Create an arbitrary set of flags. - pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY, zsa_enabled in prop::bool::ANY) -> Flags { - Flags::from_parts(spends_enabled, outputs_enabled, zsa_enabled) + prop_compose! { + /// Create an arbitrary set of flags. + pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY, zsa_enabled in prop::bool::ANY) -> Flags { + Flags::from_parts(spends_enabled, outputs_enabled, zsa_enabled) + } } - } - prop_compose! { - fn arb_base()(bytes in prop::array::uniform32(0u8..)) -> pallas::Base { - // Instead of rejecting out-of-range bytes, let's reduce them. - let mut buf = [0; 64]; - buf[..32].copy_from_slice(&bytes); - pallas::Base::from_uniform_bytes(&buf) + prop_compose! { + fn arb_base()(bytes in prop::array::uniform32(0u8..)) -> pallas::Base { + // Instead of rejecting out-of-range bytes, let's reduce them. + let mut buf = [0; 64]; + buf[..32].copy_from_slice(&bytes); + pallas::Base::from_uniform_bytes(&buf) + } } - } - prop_compose! { - /// Generate an arbitrary unauthorized bundle. This bundle does not - /// necessarily respect consensus rules; for that use - /// [`crate::builder::testing::arb_bundle`] - pub fn arb_unauthorized_bundle(n_actions: usize) - ( - flags in arb_flags(), - ) - ( - acts in vec(arb_unauthorized_action_n(n_actions, flags), n_actions), - anchor in arb_base().prop_map(Anchor::from), - flags in Just(flags), - burn in vec(arb_asset_to_burn(), 1usize..10) - ) -> Bundle { - let (balances, actions): (Vec, Vec>) = acts.into_iter().unzip(); - - Bundle::from_parts( - NonEmpty::from_vec(actions).unwrap(), - flags, - balances.into_iter().sum::>().unwrap(), - burn, - anchor, - Unauthorized, + prop_compose! { + /// Generate an arbitrary unauthorized bundle. This bundle does not + /// necessarily respect consensus rules; for that use + /// [`crate::builder::testing::arb_bundle`] + pub fn arb_unauthorized_bundle(n_actions: usize) + ( + flags in Self::arb_flags(), ) + ( + acts in vec(Self::arb_unauthorized_action_n(n_actions, flags), n_actions), + anchor in Self::arb_base().prop_map(Anchor::from), + flags in Just(flags), + burn in vec(Self::arb_asset_to_burn(), 1usize..10) + ) -> Bundle { + let (balances, actions): (Vec, Vec>) = acts.into_iter().unzip(); + + Bundle::from_parts( + NonEmpty::from_vec(actions).unwrap(), + flags, + balances.into_iter().sum::>().unwrap(), + burn, + anchor, + Unauthorized, + ) + } } - } - prop_compose! { - /// Generate an arbitrary bundle with fake authorization data. This bundle does not - /// necessarily respect consensus rules; for that use - /// [`crate::builder::testing::arb_bundle`] - pub fn arb_bundle(n_actions: usize) - ( - flags in arb_flags(), - ) - ( - acts in vec(arb_action_n(n_actions, flags), n_actions), - anchor in arb_base().prop_map(Anchor::from), - sk in arb_binding_signing_key(), - rng_seed in prop::array::uniform32(prop::num::u8::ANY), - fake_proof in vec(prop::num::u8::ANY, 1973), - fake_sighash in prop::array::uniform32(prop::num::u8::ANY), - flags in Just(flags), - burn in vec(arb_asset_to_burn(), 1usize..10) - ) -> Bundle { - let (balances, actions): (Vec, Vec>) = acts.into_iter().unzip(); - let rng = StdRng::from_seed(rng_seed); - - Bundle::from_parts( - NonEmpty::from_vec(actions).unwrap(), - flags, - balances.into_iter().sum::>().unwrap(), - burn, - anchor, - Authorized { - proof: Proof::new(fake_proof), - binding_signature: sk.sign(rng, &fake_sighash), - }, + prop_compose! { + /// Generate an arbitrary bundle with fake authorization data. This bundle does not + /// necessarily respect consensus rules; for that use + /// [`crate::builder::testing::arb_bundle`] + pub fn arb_bundle(n_actions: usize) + ( + flags in Self::arb_flags(), ) + ( + acts in vec(Self::arb_action_n(n_actions, flags), n_actions), + anchor in Self::arb_base().prop_map(Anchor::from), + sk in arb_binding_signing_key(), + rng_seed in prop::array::uniform32(prop::num::u8::ANY), + fake_proof in vec(prop::num::u8::ANY, 1973), + fake_sighash in prop::array::uniform32(prop::num::u8::ANY), + flags in Just(flags), + burn in vec(Self::arb_asset_to_burn(), 1usize..10) + ) -> Bundle { + let (balances, actions): (Vec, Vec, >) = acts.into_iter().unzip(); + let rng = StdRng::from_seed(rng_seed); + + Bundle::from_parts( + NonEmpty::from_vec(actions).unwrap(), + flags, + balances.into_iter().sum::>().unwrap(), + burn, + anchor, + Authorized { + proof: Proof::new(fake_proof), + binding_signature: sk.sign(rng, &fake_sighash), + }, + ) + } } } } diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index c60d0cd55..12a683b6d 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -4,8 +4,11 @@ use rand::{CryptoRng, RngCore}; use tracing::debug; use super::{Authorized, Bundle}; + use crate::{ + bundle::OrchardHash, circuit::VerifyingKey, + note_encryption::OrchardDomain, primitives::redpallas::{self, Binding, SpendAuth}, }; @@ -35,9 +38,9 @@ impl BatchValidator { } /// Adds the proof and RedPallas signatures from the given bundle to the validator. - pub fn add_bundle>( + pub fn add_bundle, D: OrchardDomain + OrchardHash>( &mut self, - bundle: &Bundle, + bundle: &Bundle, sighash: [u8; 32], ) { for action in bundle.actions().iter() { diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index f67b6b75a..902381933 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -2,14 +2,22 @@ use blake2b_simd::{Hash as Blake2bHash, Params, State}; -use crate::bundle::{Authorization, Authorized, Bundle}; -use crate::issuance::{IssueAuth, IssueBundle, Signed}; +use zcash_note_encryption_zsa::MEMO_SIZE; + +use crate::{ + bundle::{Authorization, Authorized, Bundle}, + issuance::{IssueAuth, IssueBundle, Signed}, + note::AssetBase, + note_encryption::OrchardDomain, + orchard_flavors::{OrchardVanilla, OrchardZSA}, +}; const ZCASH_ORCHARD_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrchardHash"; const ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActCHash"; const ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActMHash"; const ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActNHash"; const ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxAuthOrchaHash"; +const ZCASH_ORCHARD_ZSA_BURN_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcBurnHash"; const ZCASH_ORCHARD_ZSA_ISSUE_PERSONALIZATION: &[u8; 16] = b"ZTxIdSAIssueHash"; const ZCASH_ORCHARD_ZSA_ISSUE_ACTION_PERSONALIZATION: &[u8; 16] = b"ZTxIdIssuActHash"; const ZCASH_ORCHARD_ZSA_ISSUE_NOTE_PERSONALIZATION: &[u8; 16] = b"ZTxIdIAcNoteHash"; @@ -19,6 +27,40 @@ fn hasher(personal: &[u8; 16]) -> State { Params::new().hash_length(32).personal(personal).to_state() } +// FIXME: Consider not using a separate OrchardHash trait and instead move update_hash_with_burn to +// the OrchardDomain or OrchardFlavour trait. + +/// Manages the hashing of ZSA burn-related data in transactions. +pub trait OrchardHash { + /// Incorporates the hash of burn items into the main transaction hash. + fn update_hash_with_burn>( + main_hasher: &mut State, + burn_items: &[(AssetBase, V)], + ); +} + +impl OrchardHash for OrchardVanilla { + fn update_hash_with_burn>( + _main_hasher: &mut State, + _burn_items: &[(AssetBase, V)], + ) { + } +} + +impl OrchardHash for OrchardZSA { + fn update_hash_with_burn>( + main_hasher: &mut State, + burn_items: &[(AssetBase, V)], + ) { + let mut burn_hasher = hasher(ZCASH_ORCHARD_ZSA_BURN_HASH_PERSONALIZATION); + for burn_item in burn_items { + burn_hasher.update(&burn_item.0.to_bytes()); + burn_hasher.update(&burn_item.1.into().to_le_bytes()); + } + main_hasher.update(burn_hasher.finalize().as_bytes()); + } +} + /// Write disjoint parts of each Orchard shielded action as 3 separate hashes: /// * \[(nullifier, cmx, ephemeral_key, enc_ciphertext\[..52\])*\] personalized /// with ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION @@ -28,12 +70,21 @@ fn hasher(personal: &[u8; 16]) -> State { /// with ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION /// as defined in [ZIP-244: Transaction Identifier Non-Malleability][zip244] /// -/// Then, hash these together along with (flags, value_balance_orchard, anchor_orchard), -/// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION +/// These hashes are combined with additional data, which may include the ZSA burn field for specific +/// Orchard variants (see ZIP-226), flags, value_balance_orchard, and anchor_orchard. The inclusion of +/// the burn field is dependent on the specific implementation of the `OrchardHash` trait, which +/// defines whether the burn field should be included based on the variant (ZSA variant includes it). +/// +/// The final hash is personalized with ZCASH_ORCHARD_HASH_PERSONALIZATION. /// /// [zip244]: https://zips.z.cash/zip-0244 -pub(crate) fn hash_bundle_txid_data>( - bundle: &Bundle, +/// [zip226]: https://zips.z.cash/zip-0226 (for ZSA burn field hashing) +pub(crate) fn hash_bundle_txid_data< + A: Authorization, + V: Copy + Into, + D: OrchardDomain + OrchardHash, +>( + bundle: &Bundle, ) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION); @@ -44,19 +95,28 @@ pub(crate) fn hash_bundle_txid_data>( ch.update(&action.nullifier().to_bytes()); ch.update(&action.cmx().to_bytes()); ch.update(&action.encrypted_note().epk_bytes); - ch.update(&action.encrypted_note().enc_ciphertext[..84]); // TODO: make sure it is backward compatible with Orchard [..52] + ch.update(&action.encrypted_note().enc_ciphertext.as_ref()[..D::COMPACT_NOTE_SIZE]); - mh.update(&action.encrypted_note().enc_ciphertext[84..596]); + mh.update( + &action.encrypted_note().enc_ciphertext.as_ref() + [D::COMPACT_NOTE_SIZE..D::COMPACT_NOTE_SIZE + MEMO_SIZE], + ); nh.update(&action.cv_net().to_bytes()); nh.update(&<[u8; 32]>::from(action.rk())); - nh.update(&action.encrypted_note().enc_ciphertext[596..]); + nh.update( + &action.encrypted_note().enc_ciphertext.as_ref()[D::COMPACT_NOTE_SIZE + MEMO_SIZE..], + ); nh.update(&action.encrypted_note().out_ciphertext); } h.update(ch.finalize().as_bytes()); h.update(mh.finalize().as_bytes()); h.update(nh.finalize().as_bytes()); + + // Delegate complete handling of the burn data to the OrchardHash implementation + D::update_hash_with_burn(&mut h, &bundle.burn); + h.update(&[bundle.flags().to_byte()]); h.update(&(*bundle.value_balance()).into().to_le_bytes()); h.update(&bundle.anchor().to_bytes()); @@ -76,7 +136,9 @@ pub fn hash_bundle_txid_empty() -> Blake2bHash { /// Identifier Non-Malleability][zip244] /// /// [zip244]: https://zips.z.cash/zip-0244 -pub(crate) fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { +pub(crate) fn hash_bundle_auth_data( + bundle: &Bundle, +) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION); h.update(bundle.authorization().proof().as_ref()); for action in bundle.actions().iter() { diff --git a/src/circuit.rs b/src/circuit.rs index 0134ead77..9a1fd9536 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -2,36 +2,20 @@ use core::fmt; -use ff::Field; use group::{Curve, GroupEncoding}; use halo2_proofs::{ circuit::{floor_planner, Layouter, Value}, - plonk::{ - self, Advice, BatchVerifier, Column, Constraints, Expression, Instance as InstanceColumn, - Selector, SingleVerifier, - }, - poly::Rotation, + plonk::{self, BatchVerifier, SingleVerifier}, transcript::{Blake2bRead, Blake2bWrite}, }; use memuse::DynamicUsage; use pasta_curves::{arithmetic::CurveAffine, pallas, vesta}; use rand::RngCore; -use self::{ - commit_ivk::{CommitIvkChip, CommitIvkConfig}, - gadget::{ - add_chip::{AddChip, AddConfig}, - assign_free_advice, assign_is_native_asset, assign_split_flag, - }, - note_commit::{NoteCommitChip, NoteCommitConfig}, -}; use crate::{ builder::SpendInfo, bundle::Flags, - constants::{ - OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, - MERKLE_DEPTH_ORCHARD, - }, + constants::MERKLE_DEPTH_ORCHARD, keys::{ CommitIvkRandomness, DiversifiedTransmissionKey, NullifierDerivingKey, SpendValidatingKey, }, @@ -45,30 +29,12 @@ use crate::{ tree::{Anchor, MerkleHashOrchard}, value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, }; -use halo2_gadgets::{ - ecc::{ - chip::{EccChip, EccConfig}, - FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarVar, - }, - poseidon::{primitives as poseidon, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, - sinsemilla::{ - chip::{SinsemillaChip, SinsemillaConfig}, - merkle::{ - chip::{MerkleChip, MerkleConfig}, - MerklePath, - }, - }, - utilities::{ - bool_check, - cond_swap::{CondSwapChip, CondSwapConfig}, - lookup_range_check::LookupRangeCheckConfig, - }, -}; -mod commit_ivk; -pub mod gadget; -mod note_commit; -mod value_commit_orchard; +mod circuit_vanilla; +mod circuit_zsa; + +pub(in crate::circuit) mod commit_ivk; +pub(in crate::circuit) mod gadget; /// Size of the Orchard circuit. const K: u32 = 11; @@ -85,30 +51,48 @@ const ENABLE_SPEND: usize = 7; const ENABLE_OUTPUT: usize = 8; const ENABLE_ZSA: usize = 9; -/// Configuration needed to use the Orchard Action circuit. -#[derive(Clone, Debug)] -pub struct Config { - primary: Column, - q_orchard: Selector, - advices: [Column; 10], - add_config: AddConfig, - ecc_config: EccConfig, - poseidon_config: PoseidonConfig, - merkle_config_1: MerkleConfig, - merkle_config_2: MerkleConfig, - sinsemilla_config_1: - SinsemillaConfig, - sinsemilla_config_2: - SinsemillaConfig, - commit_ivk_config: CommitIvkConfig, - old_note_commit_config: NoteCommitConfig, - new_note_commit_config: NoteCommitConfig, - cond_swap_config: CondSwapConfig, +/// The `OrchardCircuit` trait defines an interface for different implementations of the PLONK circuit +/// for the different Orchard protocol flavors (Vanilla and ZSA). It serves as a bridge between +/// plonk::Circuit interfaces and specific requirements of the Orchard protocol's variations. +pub trait OrchardCircuit: Sized + Default { + /// Substitution for Config type of plonk::Circuit trait + type Config: Clone; + + /// Wrapper for configure function of plonk::Circuit trait + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config; + + /// Wrapper for configure function of plonk::Circuit trait + fn synthesize( + circuit: &CircuitBase, + config: Self::Config, + layouter: impl Layouter, + ) -> Result<(), plonk::Error>; +} + +impl plonk::Circuit for CircuitBase { + type Config = D::Config; + type FloorPlanner = floor_planner::V1; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { + D::configure(meta) + } + + fn synthesize( + &self, + config: Self::Config, + layouter: impl Layouter, + ) -> Result<(), plonk::Error> { + D::synthesize(self, config, layouter) + } } /// The Orchard Action circuit. #[derive(Clone, Debug, Default)] -pub struct Circuit { +pub struct CircuitBase { pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, pub(crate) pos: Value, pub(crate) g_d_old: Value, @@ -131,9 +115,10 @@ pub struct Circuit { pub(crate) rcv: Value, pub(crate) asset: Value, pub(crate) split_flag: Value, + phantom: std::marker::PhantomData, } -impl Circuit { +impl CircuitBase { /// This constructor is public to enable creation of custom builders. /// If you are not creating a custom builder, use [`Builder`] to compose /// and authorize a transaction. @@ -154,7 +139,7 @@ impl Circuit { output_note: Note, alpha: pallas::Scalar, rcv: ValueCommitTrapdoor, - ) -> Option { + ) -> Option> { (spend.note.nullifier(&spend.fvk) == output_note.rho()) .then(|| Self::from_action_context_unchecked(spend, output_note, alpha, rcv)) } @@ -164,7 +149,7 @@ impl Circuit { output_note: Note, alpha: pallas::Scalar, rcv: ValueCommitTrapdoor, - ) -> Circuit { + ) -> CircuitBase { let sender_address = spend.note.recipient(); let rho_old = spend.note.rho(); let psi_old = spend.note.rseed().psi(&rho_old); @@ -177,7 +162,7 @@ impl Circuit { let psi_new = output_note.rseed().psi(&rho_new); let rcm_new = output_note.rseed().rcm(&rho_new); - Circuit { + CircuitBase { path: Value::known(spend.merkle_path.auth_path()), pos: Value::known(spend.merkle_path.position()), g_d_old: Value::known(sender_address.g_d()), @@ -200,779 +185,11 @@ impl Circuit { rcv: Value::known(rcv), asset: Value::known(spend.note.asset()), split_flag: Value::known(spend.split_flag), + phantom: std::marker::PhantomData, } } } -impl plonk::Circuit for Circuit { - type Config = Config; - type FloorPlanner = floor_planner::V1; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut plonk::ConstraintSystem) -> Self::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(), - ]; - - // Constrain split_flag to be boolean - // Constrain v_old * (1 - split_flag) - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). - // Constrain (v_old = 0 and is_native_asset = 1) or (calculated root = anchor) (https://p.z.cash/ZKS:action-merkle-path-validity?partial). - // Constrain v_old = 0 or enable_spends = 1 (https://p.z.cash/ZKS:action-enable-spend). - // Constrain v_new = 0 or enable_outputs = 1 (https://p.z.cash/ZKS:action-enable-output). - // Constrain is_native_asset to be boolean - // Constraint if is_native_asset = 1 then asset = native_asset else asset != native_asset - // Constraint if split_flag = 0 then psi_old = psi_nf - // Constraint if split_flag = 1, then is_native_asset = 0 - // Constraint if enable_zsa = 0, then is_native_asset = 1 - 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_spends = meta.query_advice(advices[6], Rotation::cur()); - let enable_outputs = meta.query_advice(advices[7], Rotation::cur()); - - let split_flag = meta.query_advice(advices[8], Rotation::cur()); - - let is_native_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 native_asset = AssetBase::native() - .cv_base() - .to_affine() - .coordinates() - .unwrap(); - - let diff_asset_x = asset_x - Expression::Constant(*native_asset.x()); - let diff_asset_y = asset_y - Expression::Constant(*native_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()); - - 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_native_asset is boolean (just below), and - // * v_old is a 64 bit unsigned integer (in the note commitment evaluation). - // So, 1 - is_native_asset + v_old = 0 only when (is_native_asset = 1 and v_old = 0), no overflow can occur. - ( - "(v_old = 0 and is_native_asset = 1) or (root = anchor)", - (v_old.clone() + one.clone() - is_native_asset.clone()) * (root - anchor), - ), - ( - "v_old = 0 or enable_spends = 1", - v_old * (one.clone() - enable_spends), - ), - ( - "v_new = 0 or enable_outputs = 1", - v_new * (one.clone() - enable_outputs), - ), - ( - "bool_check is_native_asset", - bool_check(is_native_asset.clone()), - ), - ( - "(is_native_asset = 1) => (asset_x = native_asset_x)", - is_native_asset.clone() * diff_asset_x.clone(), - ), - ( - "(is_native_asset = 1) => (asset_y = native_asset_y)", - is_native_asset.clone() * diff_asset_y.clone(), - ), - // To prove that `asset` is not equal to `native_asset`, we will prove that at - // least one of `x(asset) - x(native_asset)` or `y(asset) - y(native_asset)` is - // not equal to zero. - // To prove that `x(asset) - x(native_asset)` (resp `y(asset) - y(native_asset)`) - // is not equal to zero, we will prove that it is invertible. - ( - "(is_native_asset = 0) => (asset != native_asset)", - (one.clone() - is_native_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_native_asset = 0)", - split_flag * is_native_asset.clone(), - ), - ( - "(enable_zsa = 0) => (is_native_asset = 1)", - (one.clone() - enable_zsa) * (one - is_native_asset), - ), - ], - ) - }); - - // 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 table_range_check_tag = meta.lookup_table_column(); - let lookup = ( - table_idx, - meta.lookup_table_column(), - meta.lookup_table_column(), - table_range_check_tag, - ); - - // 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. - let range_check = - LookupRangeCheckConfig::configure(meta, advices[9], table_idx, table_range_check_tag); - - // 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, - ); - 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, - ); - let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone()); - - (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()); - - let cond_swap_config = CondSwapChip::configure(meta, advices[0..5].try_into().unwrap()); - - 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, - cond_swap_config, - } - } - - #[allow(non_snake_case)] - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), plonk::Error> { - // Load the Sinsemilla generator lookup table used by the whole circuit. - SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?; - - // Construct the ECC chip. - let ecc_chip = config.ecc_chip(); - - // 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], - self.psi_nf, - )?; - - // 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.0), - )?; - - // 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, - )?; - - // Witness asset - let asset = NonIdentityPoint::new( - ecc_chip.clone(), - layouter.namespace(|| "witness asset"), - self.asset.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], - self.split_flag, - )?; - - // Witness is_native_asset which is equal to - // 1 if asset is equal to native asset, and - // 0 if asset is not equal to native asset. - let is_native_asset = assign_is_native_asset( - layouter.namespace(|| "witness is_native_asset"), - config.advices[0], - self.asset, - )?; - - // 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 = { - // 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) - }; - - 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_magnitude_sign)"), - config.sinsemilla_chip_1(), - ecc_chip.clone(), - v_net_magnitude_sign.clone(), - rcv, - 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 (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_nf, cm_old)"), - config.poseidon_chip(), - config.add_chip(), - ecc_chip.clone(), - config.cond_swap_chip(), - rho_old.clone(), - &psi_nf, - &cm_old, - nk.clone(), - 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 (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(), - config.note_commit_chip_old(), - config.cond_swap_chip(), - g_d_old.inner(), - pk_d_old.inner(), - v_old.clone(), - rho_old, - psi_old.clone(), - asset.inner(), - rcm_old, - is_native_asset.clone(), - )?; - - // Constrain derived cm_old to equal witnessed cm_old - derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; - } - - // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). - { - // Witness g_d_new - 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 - 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 = 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(), - config.note_commit_chip_new(), - config.cond_swap_chip(), - g_d_new.inner(), - pk_d_new.inner(), - v_new.clone(), - rho_new, - psi_new, - asset.inner(), - rcm_new, - is_native_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 spends", - config.primary, - ENABLE_SPEND, - config.advices[6], - 0, - )?; - - region.assign_advice_from_instance( - || "enable outputs", - config.primary, - ENABLE_OUTPUT, - config.advices[7], - 0, - )?; - - split_flag.copy_advice(|| "split_flag", &mut region, config.advices[8], 0)?; - - is_native_asset.copy_advice( - || "is_native_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_native_asset = 0, then asset != native_asset. - region.assign_advice( - || "diff_asset_x_inv", - config.advices[2], - 1, - || { - self.asset.map(|asset| { - let asset_x = *asset.cv_base().to_affine().coordinates().unwrap().x(); - let native_asset_x = *AssetBase::native() - .cv_base() - .to_affine() - .coordinates() - .unwrap() - .x(); - - let diff_asset_x = asset_x - native_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, - || { - self.asset.map(|asset| { - let asset_y = *asset.cv_base().to_affine().coordinates().unwrap().y(); - let native_asset_y = *AssetBase::native() - .cv_base() - .to_affine() - .coordinates() - .unwrap() - .y(); - - let diff_asset_y = asset_y - native_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, - )?; - - config.q_orchard.enable(&mut region, 0) - }, - )?; - - Ok(()) - } -} - /// The verifying key for the Orchard Action circuit. #[derive(Debug)] pub struct VerifyingKey { @@ -982,9 +199,9 @@ pub struct VerifyingKey { impl VerifyingKey { /// Builds the verifying key. - pub fn build() -> Self { + pub fn build() -> Self { let params = halo2_proofs::poly::commitment::Params::new(K); - let circuit: Circuit = Default::default(); + let circuit: CircuitBase = Default::default(); let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); @@ -1001,9 +218,9 @@ pub struct ProvingKey { impl ProvingKey { /// Builds the proving key. - pub fn build() -> Self { + pub fn build() -> Self { let params = halo2_proofs::poly::commitment::Params::new(K); - let circuit: Circuit = Default::default(); + let circuit: CircuitBase = Default::default(); let vk = plonk::keygen_vk(¶ms, &circuit).unwrap(); let pk = plonk::keygen_pk(¶ms, vk, &circuit).unwrap(); @@ -1053,6 +270,8 @@ impl Instance { } } + // FIXME: zcash orchard crate returns [[vesta::Scalar; 9]; 1] + // and does not assign value to instance[ENABLE_ZSA] fn to_halo2_instance(&self) -> [[vesta::Scalar; 10]; 1] { let mut instance = [vesta::Scalar::zero(); 10]; @@ -1115,9 +334,9 @@ impl DynamicUsage for Proof { impl Proof { /// Creates a proof for the given circuits and instances. - pub fn create( + pub fn create( pk: &ProvingKey, - circuits: &[Circuit], + circuits: &[CircuitBase], instances: &[Instance], mut rng: impl RngCore, ) -> Result { @@ -1179,556 +398,3 @@ impl Proof { Proof(bytes) } } - -#[cfg(test)] -mod tests { - use core::iter; - - use ff::Field; - use group::{Curve, Group, GroupEncoding}; - use halo2_proofs::{circuit::Value, dev::MockProver}; - use pasta_curves::pallas; - use rand::{rngs::OsRng, RngCore}; - - use super::{Circuit, Instance, Proof, ProvingKey, VerifyingKey, K}; - use crate::builder::SpendInfo; - use crate::bundle::Flags; - use crate::note::commitment::NoteCommitTrapdoor; - use crate::note::{AssetBase, Nullifier}; - use crate::primitives::redpallas::VerificationKey; - use crate::{ - keys::{FullViewingKey, Scope, SpendValidatingKey, SpendingKey}, - note::{Note, NoteCommitment}, - tree::MerklePath, - value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, - }; - - fn generate_dummy_circuit_instance(mut rng: R) -> (Circuit, Instance) { - let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetBase::native()); - - 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 ak: SpendValidatingKey = fvk.into(); - let alpha = pallas::Scalar::random(&mut rng); - let rk = ak.randomize(&alpha); - - let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetBase::native()); - 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, AssetBase::native()); - - let path = MerklePath::dummy(&mut rng); - let anchor = path.root(spent_note.commitment().into()); - - let psi_old = spent_note.rseed().psi(&spent_note.rho()); - - ( - Circuit { - 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(psi_old), - rcm_old: Value::known(spent_note.rseed().rcm(&spent_note.rho())), - cm_old: Value::known(spent_note.commitment()), - // For non split note, psi_nf is equal to psi_old - psi_nf: Value::known(psi_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.rseed().psi(&output_note.rho())), - rcm_new: Value::known(output_note.rseed().rcm(&output_note.rho())), - rcv: Value::known(rcv), - asset: Value::known(spent_note.asset()), - split_flag: Value::known(false), - }, - Instance { - anchor, - cv_net, - nf_old, - rk, - cmx, - enable_spend: true, - enable_output: true, - enable_zsa: false, - }, - ) - } - - // TODO: recast as a proptest - #[test] - fn round_trip() { - let mut rng = OsRng; - - let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) - .map(|()| generate_dummy_circuit_instance(&mut rng)) - .unzip(); - - let vk = VerifyingKey::build(); - - // Test that the pinned verification key (representing the circuit) - // is as expected. - { - // panic!("{:#?}", vk.vk.pinned()); - assert_eq!( - format!("{:#?}\n", vk.vk.pinned()), - include_str!("circuit_description").replace("\r\n", "\n") - ); - } - - // Test that the proof size is as expected. - let expected_proof_size = { - let circuit_cost = - halo2_proofs::dev::CircuitCost::::measure( - K, - &circuits[0], - ); - assert_eq!(usize::from(circuit_cost.proof_size(1)), 5120); - assert_eq!(usize::from(circuit_cost.proof_size(2)), 7392); - 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(); - 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 serialized_proof_test_case() { - use std::fs; - use std::io::{Read, Write}; - - let vk = VerifyingKey::build(); - - fn write_test_case( - mut w: W, - instance: &Instance, - proof: &Proof, - ) -> 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.clone()))?; - w.write_all(&instance.cmx.to_bytes())?; - w.write_all(&[ - u8::from(instance.enable_spend), - u8::from(instance.enable_output), - u8::from(instance.enable_zsa), - ])?; - - w.write_all(proof.as_ref())?; - Ok(()) - } - - fn read_test_case(mut r: R) -> 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 enable_zsa = read_bool(&mut r); - let instance = Instance::from_parts( - anchor, - cv_net, - nf_old, - rk, - cmx, - Flags::from_parts(enable_spend, enable_output, enable_zsa), - ); - - let mut proof_bytes = vec![]; - r.read_to_end(&mut proof_bytes)?; - let proof = Proof::new(proof_bytes); - - Ok((instance, proof)) - } - - 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, instance) = generate_dummy_circuit_instance(OsRng); - let instances = &[instance.clone()]; - - let pk = ProvingKey::build(); - let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); - assert!(proof.verify(&vk, instances).is_ok()); - - let file = std::fs::File::create("src/circuit_proof_test_case.bin")?; - write_test_case(file, &instance, &proof) - }; - create_proof().expect("should be able to write new proof"); - } - - // Parse the hardcoded proof test case. - let (instance, proof) = { - let test_case_bytes = fs::read("src/circuit_proof_test_case.bin").unwrap(); - read_test_case(&test_case_bytes[..]).expect("proof must be valid") - }; - assert_eq!(proof.0.len(), 5120); - - 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 { - 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(), - psi_nf: 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(), - asset: Value::unknown(), - split_flag: Value::unknown(), - }; - halo2_proofs::dev::CircuitLayout::default() - .show_labels(false) - .view_height(0..(1 << 11)) - .render(K, &circuit, &root) - .unwrap(); - } - - fn check_proof_of_orchard_circuit(circuit: &Circuit, instance: &Instance, should_pass: bool) { - let proof_verify = MockProver::run( - K, - circuit, - instance - .to_halo2_instance() - .iter() - .map(|p| p.to_vec()) - .collect(), - ) - .unwrap() - .verify(); - if should_pass { - assert!(proof_verify.is_ok()); - } else { - assert!(proof_verify.is_err()); - } - } - - fn generate_circuit_instance( - is_native_asset: bool, - split_flag: bool, - mut rng: R, - ) -> (Circuit, Instance) { - // Create asset - let asset_base = if is_native_asset { - AssetBase::native() - } else { - AssetBase::random() - }; - - // Create spent_note - let (spent_note_fvk, spent_note) = { - let sk = SpendingKey::random(&mut rng); - let fvk: FullViewingKey = (&sk).into(); - let sender_address = fvk.address_at(0u32, Scope::External); - let rho_old = Nullifier::dummy(&mut rng); - let note = Note::new( - sender_address, - NoteValue::from_raw(40), - asset_base, - rho_old, - &mut rng, - ); - let spent_note = if split_flag { - note.create_split_note(&mut rng) - } else { - note - }; - (fvk, spent_note) - }; - - let output_value = NoteValue::from_raw(10); - - let (scope, v_net) = if split_flag { - ( - Scope::External, - // Split notes do not contribute to v_net. - // Therefore, if split_flag is true, v_net = - output_value - NoteValue::zero() - output_value, - ) - } else { - ( - spent_note_fvk - .scope_for_address(&spent_note.recipient()) - .unwrap(), - spent_note.value() - output_value, - ) - }; - - let nf_old = spent_note.nullifier(&spent_note_fvk); - let ak: SpendValidatingKey = spent_note_fvk.clone().into(); - let alpha = pallas::Scalar::random(&mut rng); - let rk = ak.randomize(&alpha); - - let output_note = { - let sk = SpendingKey::random(&mut rng); - let fvk: FullViewingKey = (&sk).into(); - let sender_address = fvk.address_at(0u32, Scope::External); - - Note::new(sender_address, output_value, asset_base, nf_old, &mut rng) - }; - - let cmx = output_note.commitment().into(); - - let rcv = ValueCommitTrapdoor::random(&mut rng); - let cv_net = ValueCommitment::derive(v_net, rcv, asset_base); - - let path = MerklePath::dummy(&mut rng); - let anchor = path.root(spent_note.commitment().into()); - - let spend_info = SpendInfo { - dummy_sk: None, - fvk: spent_note_fvk, - scope, - note: spent_note, - merkle_path: path, - split_flag, - }; - - ( - Circuit::from_action_context_unchecked(spend_info, output_note, alpha, rcv), - Instance { - anchor, - cv_net, - nf_old, - rk, - cmx, - enable_spend: true, - enable_output: true, - enable_zsa: true, - }, - ) - } - - fn random_note_commitment(mut rng: impl RngCore) -> NoteCommitment { - NoteCommitment::derive( - pallas::Point::random(&mut rng).to_affine().to_bytes(), - pallas::Point::random(&mut rng).to_affine().to_bytes(), - NoteValue::from_raw(rng.next_u64()), - AssetBase::random(), - pallas::Base::random(&mut rng), - pallas::Base::random(&mut rng), - NoteCommitTrapdoor(pallas::Scalar::random(&mut rng)), - ) - .unwrap() - } - - #[test] - fn orchard_circuit_negative_test() { - let mut rng = OsRng; - - for is_native_asset in [true, false] { - for split_flag in [true, false] { - let (circuit, instance) = - generate_circuit_instance(is_native_asset, split_flag, &mut rng); - - let should_pass = !(matches!((is_native_asset, split_flag), (true, true))); - - check_proof_of_orchard_circuit(&circuit, &instance, should_pass); - - // Set cv_net to be zero - // The proof should fail - let instance_wrong_cv_net = Instance { - anchor: instance.anchor, - cv_net: ValueCommitment::from_bytes(&[0u8; 32]).unwrap(), - nf_old: instance.nf_old, - rk: instance.rk.clone(), - cmx: instance.cmx, - enable_spend: instance.enable_spend, - enable_output: instance.enable_output, - enable_zsa: instance.enable_zsa, - }; - check_proof_of_orchard_circuit(&circuit, &instance_wrong_cv_net, false); - - // Set rk_pub to be a dummy VerificationKey - // The proof should fail - let instance_wrong_rk = Instance { - anchor: instance.anchor, - cv_net: instance.cv_net.clone(), - nf_old: instance.nf_old, - rk: VerificationKey::dummy(), - cmx: instance.cmx, - enable_spend: instance.enable_spend, - enable_output: instance.enable_output, - enable_zsa: instance.enable_zsa, - }; - check_proof_of_orchard_circuit(&circuit, &instance_wrong_rk, false); - - // Set cm_old to be a random NoteCommitment - // The proof should fail - let circuit_wrong_cm_old = Circuit { - path: circuit.path, - pos: circuit.pos, - g_d_old: circuit.g_d_old, - pk_d_old: circuit.pk_d_old, - v_old: circuit.v_old, - rho_old: circuit.rho_old, - psi_old: circuit.psi_old, - rcm_old: circuit.rcm_old.clone(), - cm_old: Value::known(random_note_commitment(&mut rng)), - psi_nf: circuit.psi_nf, - alpha: circuit.alpha, - ak: circuit.ak.clone(), - nk: circuit.nk, - rivk: circuit.rivk, - g_d_new: circuit.g_d_new, - pk_d_new: circuit.pk_d_new, - v_new: circuit.v_new, - psi_new: circuit.psi_new, - rcm_new: circuit.rcm_new.clone(), - rcv: circuit.rcv, - asset: circuit.asset, - split_flag: circuit.split_flag, - }; - check_proof_of_orchard_circuit(&circuit_wrong_cm_old, &instance, false); - - // Set cmx_pub to be a random NoteCommitment - // The proof should fail - let instance_wrong_cmx_pub = Instance { - anchor: instance.anchor, - cv_net: instance.cv_net.clone(), - nf_old: instance.nf_old, - rk: instance.rk.clone(), - cmx: random_note_commitment(&mut rng).into(), - enable_spend: instance.enable_spend, - enable_output: instance.enable_output, - enable_zsa: instance.enable_zsa, - }; - check_proof_of_orchard_circuit(&circuit, &instance_wrong_cmx_pub, false); - - // Set nf_old_pub to be a random Nullifier - // The proof should fail - let instance_wrong_nf_old_pub = Instance { - anchor: instance.anchor, - cv_net: instance.cv_net.clone(), - nf_old: Nullifier::dummy(&mut rng), - rk: instance.rk.clone(), - cmx: instance.cmx, - enable_spend: instance.enable_spend, - enable_output: instance.enable_output, - enable_zsa: instance.enable_zsa, - }; - check_proof_of_orchard_circuit(&circuit, &instance_wrong_nf_old_pub, false); - - // If split_flag = 0 , set psi_nf to be a random Pallas base element - // The proof should fail - if !split_flag { - let circuit_wrong_psi_nf = Circuit { - path: circuit.path, - pos: circuit.pos, - g_d_old: circuit.g_d_old, - pk_d_old: circuit.pk_d_old, - v_old: circuit.v_old, - rho_old: circuit.rho_old, - psi_old: circuit.psi_old, - rcm_old: circuit.rcm_old.clone(), - cm_old: circuit.cm_old.clone(), - psi_nf: Value::known(pallas::Base::random(&mut rng)), - alpha: circuit.alpha, - ak: circuit.ak.clone(), - nk: circuit.nk, - rivk: circuit.rivk, - g_d_new: circuit.g_d_new, - pk_d_new: circuit.pk_d_new, - v_new: circuit.v_new, - psi_new: circuit.psi_new, - rcm_new: circuit.rcm_new.clone(), - rcv: circuit.rcv, - asset: circuit.asset, - split_flag: circuit.split_flag, - }; - check_proof_of_orchard_circuit(&circuit_wrong_psi_nf, &instance, false); - } - - // If asset is not equal to the native asset, set enable_zsa = 0 - // The proof should fail - if !is_native_asset { - let instance_wrong_enable_zsa = Instance { - anchor: instance.anchor, - cv_net: instance.cv_net.clone(), - nf_old: instance.nf_old, - rk: instance.rk.clone(), - cmx: instance.cmx, - enable_spend: instance.enable_spend, - enable_output: instance.enable_output, - enable_zsa: false, - }; - check_proof_of_orchard_circuit(&circuit, &instance_wrong_enable_zsa, false); - } - } - } - } -} diff --git a/src/circuit/circuit_description_vanilla b/src/circuit/circuit_description_vanilla new file mode 100644 index 000000000..8bb3759ed --- /dev/null +++ b/src/circuit/circuit_description_vanilla @@ -0,0 +1,28140 @@ +PinnedVerificationKey { + base_modulus: "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + scalar_modulus: "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + domain: PinnedEvaluationDomain { + k: 11, + extended_k: 14, + omega: 0x181b50ad5f32119e31cbd395426d600b7a9b88bcaaa1c24eef28545aada17813, + }, + cs: PinnedConstraintSystem { + num_fixed_columns: 29, + num_advice_columns: 10, + num_instance_columns: 1, + num_selectors: 56, + gates: [ + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 0, + column_index: 0, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 0, + column_index: 0, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Scaled( + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + Advice { + query_index: 10, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 19, + column_index: 19, + 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( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 11, + 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: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 11, + 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: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 11, + 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: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 11, + 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: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 18, + column_index: 18, + 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( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 20, + 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: 20, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 20, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 18, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 18, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 0x2000000000000000000000000000000011234c7e04a67c8dcc96987680000001, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 20, + 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: 20, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 20, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 18, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 18, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Advice { + query_index: 21, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 11, + column_index: 9, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + ), + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 21, + column_index: 1, + rotation: Rotation( + -1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 20, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 23, + column_index: 7, + rotation: Rotation( + -1, + ), + }, + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 20, + column_index: 20, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Advice { + query_index: 18, + column_index: 7, + 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( + Sum( + Advice { + query_index: 10, + 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: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + ), + ), + ), + ), + ), + 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( + Product( + Sum( + Advice { + query_index: 10, + 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: 10, + 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: 16, + column_index: 0, + 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, + ), + }, + ), + ), + ), + Sum( + Product( + Sum( + Advice { + query_index: 10, + 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: 10, + 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: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Fixed { + query_index: 22, + column_index: 22, + 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: 22, + column_index: 22, + 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: 3, + 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: 4, + 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: 5, + 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: 6, + 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: 7, + 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: 8, + 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: 9, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 22, + column_index: 22, + 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: 2, + column_index: 11, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + 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( + Fixed { + query_index: 23, + column_index: 23, + 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: 3, + 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: 4, + 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: 5, + 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: 6, + 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: 7, + 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: 8, + 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: 9, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 23, + column_index: 23, + 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: 2, + column_index: 11, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Fixed { + query_index: 23, + column_index: 23, + 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: 23, + column_index: 23, + 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( + 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, + ), + }, + ), + ), + ), + 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( + 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( + 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( + 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, + ), + }, + ), + ), + ), + 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( + 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( + 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( + 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, + ), + }, + ), + ), + ), + 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( + 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, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Negated( + Product( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + Constant( + 0x0000000000000000000000000000000001000000000000000000000000000000, + ), + ), + ), + ), + ), + ), + 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, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Product( + Sum( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Sum( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Negated( + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000008, + ), + ), + ), + ), + ), + ), + ), + ), + 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, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 22, + column_index: 6, + 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( + 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, + ), + }, + ), + ), + ), + 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( + 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, + ), + }, + ), + ), + ), + 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( + 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( + 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( + 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( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Negated( + Sum( + Sum( + Sum( + Advice { + query_index: 20, + 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: 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, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + 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: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Negated( + Advice { + query_index: 22, + column_index: 6, + 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( + 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( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + 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: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x22f5b5e1e6081c9774938717989a19579aad3d8262efd83ff84d806f685f747a, + ), + ), + Negated( + Advice { + query_index: 18, + 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( + 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( + Sum( + Sum( + Scaled( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + 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: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x3bf763086a18936451e0cbead65516b975872c39b59a31f615639415f6e85ef1, + ), + ), + Negated( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + 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( + Product( + Product( + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Product( + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + 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( + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 7, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 7, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 7, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 7, + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x31916628e58a5abb293f0f0d886c7954240d4a7cbf7357368eca5596e996ab5e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x07c045d5f5e9e5a6d803952bbb364fdfa0a3b71a5fb1573519d1cf25d8e8345d, + ), + ), + Fixed { + query_index: 7, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + ), + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x2cc057f3fa14687acc59ffd00de864434543705f35e98ab5c6de463cd1404e6b, + ), + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x32e7c439f2f967e55fd72b55df208385fadbf8ae7ae24796171840417cab7576, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x2eae5df8c3115969f461778abf6c91fa1403db6f50302040942645bd7d4464e0, + ), + ), + ), + ), + ), + 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( + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x25cae2599892a8b0b36664548d60957d78f8365c85bbab07402270113e047a2e, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x22f5b5e1e6081c9774938717989a19579aad3d8262efd83ff84d806f685f747a, + ), + ), + Fixed { + query_index: 8, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x07bf368481067199db18b4aefe68d26d13f074fde9a18b29a1ca1516a4a1a6a0, + ), + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x2aec6906c63f3cf1018a918b9dac5dadbb1d65040c85c1bfe82425bc1b23a059, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0952e0243aec2af01215944a64a246b276b2a7139db71b36e0541adf238e0781, + ), + ), + ), + ), + ), + 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( + 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: 5, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + 0x1d1aab4ec1cd678892d15e7dceef1665cbeaf48b3a0624c3c771effa43263664, + ), + ), + Scaled( + Sum( + Advice { + query_index: 8, + column_index: 8, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 6, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + ), + 0x3bf763086a18936451e0cbead65516b975872c39b59a31f615639415f6e85ef1, + ), + ), + Fixed { + query_index: 9, + column_index: 10, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Sum( + Sum( + Scaled( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x2fcbba6f9159a219723a63a0c09dab26aef9112e952fdbb52a418d8d73a7c908, + ), + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x1ec7372574f3851bb4ddd4b4d6452256c5e4960d7424cd3776efab42d4fba90b, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0d0c2efd6472f12a3c26fa4b7d25b1e487a7435d30f8be81adc8933c6f3c72ee, + ), + ), + ), + ), + ), + 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( + Sum( + Advice { + query_index: 20, + column_index: 6, + rotation: Rotation( + -1, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Negated( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + 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( + 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: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + 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( + Advice { + query_index: 24, + column_index: 8, + rotation: Rotation( + -1, + ), + }, + Negated( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + 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( + Scaled( + Fixed { + query_index: 0, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + 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: 16, + column_index: 16, + 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: 16, + 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: 16, + column_index: 16, + 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: 16, + 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: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 10, + column_index: 12, + 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: 16, + 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: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Scaled( + Product( + Fixed { + query_index: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 13, + column_index: 3, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + 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( + 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: 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( + 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: 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, + ), + }, + ), + ), + ), + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Negated( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + Scaled( + Sum( + Sum( + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 17, + 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( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 17, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Scaled( + Fixed { + query_index: 3, + column_index: 4, + rotation: Rotation( + 0, + ), + }, + 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: 17, + column_index: 17, + 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: 17, + column_index: 17, + 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: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Product( + Sum( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + Sum( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Sum( + Product( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + Negated( + Advice { + query_index: 15, + column_index: 5, + rotation: Rotation( + 1, + ), + }, + ), + ), + Negated( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + ), + ), + Product( + Scaled( + Product( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + Scaled( + Advice { + query_index: 18, + 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( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Advice { + query_index: 19, + 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( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + Negated( + Sum( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 17, + 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: 16, + column_index: 0, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Sum( + Sum( + Sum( + Advice { + query_index: 5, + column_index: 5, + rotation: Rotation( + 0, + ), + }, + Scaled( + Advice { + query_index: 17, + column_index: 1, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Constant( + 0x0000000000000000000000000000100000000000000000000000000000000000, + ), + ), + Negated( + Constant( + 0x00000000000000000000000000000000224698fc094cf91b992d30ed00000001, + ), + ), + ), + Negated( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 14, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 25, + column_index: 25, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 18, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 18, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + Product( + Product( + Product( + Product( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000003, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 26, + column_index: 26, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0200000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000020, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000040, + ), + ), + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + ), + Scaled( + Advice { + query_index: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + Product( + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000004, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000005, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 27, + column_index: 27, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000002, + ), + ), + Scaled( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + 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( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0200000000000000000000000000000000000000000000000000000000000000, + ), + ), + Scaled( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000007, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + 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( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 22, + column_index: 6, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 19, + column_index: 8, + rotation: Rotation( + 1, + ), + }, + ), + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + 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: 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + ), + Product( + 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( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000006, + ), + Negated( + Fixed { + query_index: 28, + column_index: 28, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + Product( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 10, + 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: 9, + column_type: Advice, + }, + Rotation( + 1, + ), + ), + ( + Column { + index: 9, + 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: 0, + 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, + ), + ), + ( + Column { + index: 6, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 1, + column_type: Advice, + }, + Rotation( + -1, + ), + ), + ( + Column { + index: 6, + 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: 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: 12, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 1, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 2, + column_type: Fixed, + }, + Rotation( + 0, + ), + ), + ( + Column { + index: 13, + 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, + ), + ), + ], + 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: 14, + column_index: 14, + rotation: Rotation( + 0, + ), + }, + Sum( + Product( + Fixed { + query_index: 15, + column_index: 15, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Advice { + query_index: 10, + column_index: 9, + rotation: Rotation( + 1, + ), + }, + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Product( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 15, + column_index: 15, + rotation: Rotation( + 0, + ), + }, + ), + ), + Advice { + query_index: 9, + column_index: 9, + rotation: Rotation( + 0, + ), + }, + ), + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + ], + }, + Argument { + input_expressions: [ + Product( + Fixed { + query_index: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 2, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Fixed { + query_index: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Negated( + Product( + Fixed { + query_index: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 10, + column_index: 12, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Advice { + query_index: 12, + column_index: 2, + rotation: Rotation( + 1, + ), + }, + ), + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x0db5218be6881f0f1431d4ea7d4afc7b29a05bafbede62b55a91eb912044ea5f, + ), + ), + Sum( + Product( + Fixed { + query_index: 16, + column_index: 16, + 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: 16, + column_index: 16, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x2f0f40c2f152a01c9caf66298493d5d0944a041c2e65ba0117c24f76bf8e6483, + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 11, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 12, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ], + }, + Argument { + input_expressions: [ + Product( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + Sum( + Advice { + query_index: 7, + column_index: 7, + rotation: Rotation( + 0, + ), + }, + Negated( + Scaled( + Product( + Sum( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Product( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Sum( + Fixed { + query_index: 13, + column_index: 13, + rotation: Rotation( + 0, + ), + }, + Negated( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + ), + ), + ), + ), + ), + Advice { + query_index: 18, + column_index: 7, + rotation: Rotation( + 1, + ), + }, + ), + 0x0000000000000000000000000000000000000000000000000000000000000400, + ), + ), + ), + ), + Sum( + Product( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 6, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + ), + Scaled( + Sum( + Constant( + 0x0000000000000000000000000000000000000000000000000000000000000001, + ), + Negated( + Fixed { + query_index: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x0db5218be6881f0f1431d4ea7d4afc7b29a05bafbede62b55a91eb912044ea5f, + ), + ), + Sum( + Product( + Fixed { + query_index: 17, + column_index: 17, + 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: 17, + column_index: 17, + rotation: Rotation( + 0, + ), + }, + ), + ), + 0x2f0f40c2f152a01c9caf66298493d5d0944a041c2e65ba0117c24f76bf8e6483, + ), + ), + ], + table_expressions: [ + Fixed { + query_index: 1, + column_index: 0, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 11, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 12, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ], + }, + ], + constants: [ + Column { + index: 3, + column_type: Fixed, + }, + ], + minimum_degree: None, + }, + fixed_commitments: [ + (0x05f5862cad2888855bc3c1843a9eff57b11b592d9eb0e13354256661387f5231, 0x32236b14df85bf5f532a930232cb23a5c56ef7d67aaeed8bcb8fc10ea132cbd6), + (0x3e727e8554679f98120355d39d958dbd8755d5a7f8b42ea87f258064c4b3eb57, 0x0c0d5c23f3ee62ac1b2d986dd3033bbcab260d590e1393de3a14a4b31ae091bb), + (0x3748680dd6a91c5dec668b30fb6bf9a450aeb884b81cbc344914f265760e7131, 0x18530eaa5c58b61bd3fc38220ea484c0922524a815a8f752be955c229f9c4164), + (0x18cd12d5f4e12bd7247a8fd7cc93ded7a8a9b60935b319b2db578a8fceef9559, 0x16b15af4fcfb9ed75533e7a4c1966cae5621f10dc1dfbba39a491ec37c911b5e), + (0x37e70146801841d95832259e4d8f39aeee8a364ea5007f82aa84a0e387231315, 0x20ef65833381c985722a95e65125a1123cab3357741168a5ed7e92e972dbd30d), + (0x12e3af8e507a2ca30e544568cf319701ecbc29dc5919f0198d541938edecc8f3, 0x3fb1bb2804faaa4c215e93694d5d1e6f87874cb8c34cb9206ff958de14e44275), + (0x334d99f316343f01c8eb67e327c76f743f8de7f957c893c6f30ccd87e5d1af3a, 0x1da64caf127c8eb513653791147a85ed1edcca12935c95b7d615a9377c9406d8), + (0x1430dca15080286939046d3cb9fb7ace4ed1da2fbe7a362e8f3575489bc6e3e1, 0x1ef5f4aff2902f3bc8acb2cc884a2d44334f910a68b2701f1c37943652b46d8f), + (0x02ebdeac7e91b3d15e4b7c0533e42678672ec384d51e51e22342be7edeeb8074, 0x03638952e0489de03ff03236433f5d2617cb4ddd0a72637ed31095deca85a2a6), + (0x31179b7f5b01ad2a55cf9a66f57b696e9b9cb4919cca426d469b270827f3018b, 0x2b231a9a71b674cc546ba9b36916a13411648903cfdcb808926fc47ee745586c), + (0x0673497660cec8a8c391cfb16f5ffdcb710f9e9e194a1a85935cf4bc45b01359, 0x15ce8801c51811f0256a43f762e497fe38c88866c09bb256289d93e0393dc225), + (0x098d2a0cfedae91fe29e21a01b5e43045b9cc8d9a334f1aee3e075f36a16e323, 0x0be47cc41643c9e6d95012428d5b46ae5a44e765392e05152a0181e30689d8b6), + (0x02adb7cbc9ebbbd87d7d6a41fc40cb4cf57585c6243aa204f757c9026ef20fd3, 0x327fc06fee179c6a57ed95336f9fb7854420d80dd191251a40935664ff6c8067), + (0x2d00d4ec8aa5e4b3d035131f559e4a97f896e8dbc39badb92b58a8d46b5e91df, 0x37046fb32ed8eb4ba0b4da8e1c9b56cd3832fa2ed4788f7faf4fee1f76a94c32), + (0x212f5afd70e787e2fd951e0ddc5430d1fa78f988c30740384d18cf9ff276b43b, 0x20c5a150e200caddf9a35a993668fc4742be5d924d1086f05c74ef6a78b5feb2), + (0x02c283cbde85f2ad26daddeabe01b1574ce7de68f0e329ec95a4154dd4713f29, 0x208cf96aa5255b543933ee3e9bdd054d4f15265a7c8921aaee89c192af2fd284), + (0x1f777f0e4263ec4a19f30813739c640335ffa951cc3cc586b6c4095e737f95be, 0x061c07fb12cb19582eefd858a08e689acd970c8cb9ed8f7b928d88e691a2f586), + (0x13d0bd76da4ace22c0e90b098d6073551322b8c734bf37eeca67fbf19687f550, 0x3d996cc9da5a31cefb453390b906eabbcc75797bc6a6b9c9e3af2fe7b6b8beed), + (0x04cad7405b492a30db0a710c842cecc97d02059acf4c02aa79626dce68ac4837, 0x3d6d7b6698b258e61ebe0b25d9cbcd4a016cb0a2ae8d92752532d98cfb27720d), + (0x1b6f5383c5a0ae5bf457e1c8e17a933e892464d33fef9e9262411e01c117d87e, 0x0c552b960e8ce365b5f2302bcc0b7ce5cdf964e6036602cfc859c8769184f619), + (0x3fa4b5cc33e30de3ac7342c84f47f4fffe7ae77dda1689c2f08050d0ab743cb1, 0x327663e39b128ec28c94486b1e5d4429000626f65230ed572c66f80406a42176), + (0x2184a7d65b5000cc5c5f178c2f4ab5b11a67fdc626771c29ade508020c8da032, 0x34c98ee1f6dfa6c1e8cd915d1efeb484206e13e5e32e13118925913568e620b7), + (0x0974ad1a3c0eb4c8d2c59cd820a82b7f28ea2f7a245008d403815131ff30879e, 0x00bb593cdf920cef4965f788d65eba3c3aa07d9718dfb62e3e385849a0d692a8), + (0x1e355d783cffccafc120f462461fb312773442762383ac444009653f3d8d4be6, 0x3c60e17b18492aa2c41798b409d2bcc1857ca57ee9d2fb0001584cedc8e141d6), + (0x0a6fe1cc1ce659681079768ca8ff94d82c7d51ef39cd99b738b144de3a3027f6, 0x30cfc2f4e0ec95f623199970d8b762647ad2d7c3591a20781ee8187702babe5f), + (0x00d87a2c430f1db50a63f18f8cf8807f4f70d3acb940d4130ba6811f8ba2d479, 0x13d5742320e1b2cecda6073b7f2bf5816b9067453deeaa829f356a65ef5621b2), + (0x3118979ade023f3977d034f86eed6506d7e0586ead81f80bc5ca01a7660ee0c9, 0x30f6731193d5c786cf61b05523c05e2664a066c2d39a685588f02883057320ad), + (0x0df51bd411d5f95da66fcc57f5e4d8dbcca3d82b08ceb61e9ff1befa074e08d3, 0x11c9092b6d02c46f173b0108854499ca4922afaf58e0734e77a6088715e84b64), + (0x24289014ede2672df8e8e32eb4e0d71709846041319fb85b1328cdb3b8764565, 0x0833de9c0b76ae816df0e41ae33daece27c63a41f2ba9abbbc7c08724211b50c), + ], + permutation: VerifyingKey { + commitments: [ + (0x2ad778f0e75a3dcad7c0cc2215e554f3d6fe41eabd612c487ea2708d59fb2e7e, 0x0561e9268230b53ec9cac0fd7654b3edaa3851f624c62bdae39519ae17526c06), + (0x358a21858e7f0da213959badd192b12e7bd40f6b18f5617a7fbad1f142b53c41, 0x1cc665c7a95332ea3ecb79bc35c4d672837809470691ad6a96f2eca081ca9c98), + (0x28d6468db328f1d201b3b7ca36f3affddee9dd0043d8868e34f1839742ac3190, 0x2f38eba3a82748cc28e46c1e20b7d343fdac7ef726ed6de89f6484c6070204f1), + (0x21f27b52cd9a76e1dbbf572fbfc0262007015777b68bda954f3c9db60ebb27f9, 0x0dbbf8f04e8476544a853e54093689d59935de9194eef24a0ee04d6faef9423f), + (0x0253a69e81add6bc1c2fe14bd90dab3e3c2603747dd3760c9dd1e341d96a79ed, 0x15cbe812a45a46512cc8ed493250f75a9dcaaee4be0d3bdaee8b43d74c50481f), + (0x19eb8760e7d0e6ae6d28d65627d958661cdde4e58a0aeb55a6b7017bcf723002, 0x064575794bf9bfdbc50e94b8afbbd2851ae4e12ff2809777d28fe71c235727d9), + (0x0e5c408b5224841cb4f75aec5cdb7bc95c150bbe858dbde8dbac9f72e5392462, 0x01030c69ac5fc7dd63e4e0bb1718d26f51b79dccc81e0b172e98c26e59145414), + (0x12437cb05ecff24131b52b5a55f6f143d8437c28c9d7c31eb06cfa093972a64b, 0x06e1a5e39566b4ce097a6c7dace6dcb827e54dac7d64fa79d994fb1557717614), + (0x34636ff9de412da15f41a8a006abbe8f43a5cffc94e6c3deb02f98af4fb2b8c7, 0x0270f0c3fa8cc7338f20fbcd5ec4e62799e051e0c5938d9e8c41581de8da6165), + (0x218e047b1c0a3b92c59539b3f6d9c23d34ebeeb65ca0be98f5e0e9642bdf1085, 0x20c1117f40b375688a94ff5c5c0b70004e43c7c7cd492fe8055fea081ea5ca78), + (0x2478c8226d4ede1c203fa7455b5fe28f99d5a0cb8ccdb5be4b54d5edcce974c4, 0x1ce69b76f05daeae57cd3d452370439237da89f2ddc84f7b2e35703acbf99655), + (0x08383138ecc6f2fb5459043c7666ae3df7802f1f02392af44db6ba25cd7d2c56, 0x20957d7a3f00a8589f627c5f5e471f45a84fbcbdcde00dfc97b9a97f3f723202), + (0x21d210b41675a1eae44cbd0f3fd27d69e30716c71873f6089cee61acacd403ab, 0x2275e97c7e84f68bfaa528a9d8be4e059f7abefd80d03fbfca774e8414a9b7c1), + (0x0f9e7de28e0f650d99d99d95c0fcd39c9dac9db5aa1973319f66922d6eb9f7d5, 0x1ba644ecc18ad711ddd33af7f695f6834e9f35c93d47a6a5273dabbe800fc7e6), + (0x0aab3ab73afac76277cd94a891de15e42ceb09f3a9865dab5c814bebfbb4453f, 0x27119fec3736d99abeeef1ad7b857db7e754e0c158780ed3dd0cdd4dc2453e10), + ], + }, +} diff --git a/src/circuit_description b/src/circuit/circuit_description_zsa similarity index 100% rename from src/circuit_description rename to src/circuit/circuit_description_zsa diff --git a/src/circuit/circuit_proof_test_case_vanilla.bin b/src/circuit/circuit_proof_test_case_vanilla.bin new file mode 100644 index 000000000..9dcabe5b6 Binary files /dev/null and b/src/circuit/circuit_proof_test_case_vanilla.bin differ diff --git a/src/circuit_proof_test_case.bin b/src/circuit/circuit_proof_test_case_zsa.bin similarity index 100% rename from src/circuit_proof_test_case.bin rename to src/circuit/circuit_proof_test_case_zsa.bin diff --git a/src/circuit/circuit_vanilla.rs b/src/circuit/circuit_vanilla.rs new file mode 100644 index 000000000..ee76497ba --- /dev/null +++ b/src/circuit/circuit_vanilla.rs @@ -0,0 +1,922 @@ +//! The Orchard Action circuit implementation. + +// FIXME: rename to vanilla.rs (as it's alredy in circuit folder)? + +use group::Curve; + +use pasta_curves::pallas; + +use halo2_gadgets::{ + ecc::{ + chip::{EccChip, EccConfig}, + FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarFixedShort, ScalarVar, + }, + poseidon::{primitives as poseidon, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, + sinsemilla::{ + chip::{SinsemillaChip, SinsemillaConfig}, + merkle::{ + chip::{MerkleChip, MerkleConfig}, + MerklePath, + }, + }, + utilities::lookup_range_check::LookupRangeCheckConfig, +}; + +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::{self, Advice, Column, Constraints, Expression, Instance as InstanceColumn, Selector}, + poly::Rotation, +}; + +use crate::{ + constants::{ + OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, + }, + orchard_flavors::OrchardVanilla, +}; + +use super::{ + commit_ivk::{self, CommitIvkChip, CommitIvkConfig}, + gadget::{ + add_chip::{self, AddChip, AddConfig}, + AddInstruction, + }, + CircuitBase, OrchardCircuit, ANCHOR, CMX, CV_NET_X, CV_NET_Y, ENABLE_OUTPUT, ENABLE_SPEND, + NF_OLD, RK_X, RK_Y, +}; + +use self::{ + gadget::assign_free_advice, + note_commit::{NoteCommitChip, NoteCommitConfig}, +}; + +mod gadget; +mod note_commit; + +/// Configuration needed to use the Orchard Action circuit. +#[derive(Clone, Debug)] +pub struct Config { + primary: Column, + q_orchard: Selector, + advices: [Column; 10], + add_config: AddConfig, + ecc_config: EccConfig, + poseidon_config: PoseidonConfig, + merkle_config_1: MerkleConfig, + merkle_config_2: MerkleConfig, + sinsemilla_config_1: + SinsemillaConfig, + sinsemilla_config_2: + SinsemillaConfig, + commit_ivk_config: CommitIvkConfig, + old_note_commit_config: NoteCommitConfig, + new_note_commit_config: NoteCommitConfig, +} + +impl OrchardCircuit for OrchardVanilla { + type Config = Config; + + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::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(), + ]; + + // 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_spends = 1 (https://p.z.cash/ZKS:action-enable-spend). + // Constrain v_new = 0 or enable_outputs = 1 (https://p.z.cash/ZKS:action-enable-output). + 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_spends = meta.query_advice(advices[6], Rotation::cur()); + let enable_outputs = 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_spends = 1", + v_old * (one.clone() - enable_spends), + ), + ( + "v_new = 0 or enable_outputs = 1", + v_new * (one - enable_outputs), + ), + ], + ) + }); + + // 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(), + None, + ); + + // 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. + let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx, None); + + // 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, + ); + 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, + ); + let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone()); + + (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, + } + } + + #[allow(non_snake_case)] + fn synthesize( + circuit: &CircuitBase, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), plonk::Error> { + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?; + + // Construct the ECC chip. + let ecc_chip = config.ecc_chip(); + + // 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], + circuit.psi_old, + )?; + + // Witness rho_old + let rho_old = assign_free_advice( + layouter.namespace(|| "witness rho_old"), + config.advices[0], + circuit.rho_old.map(|rho| rho.0), + )?; + + // Witness cm_old + let cm_old = Point::new( + ecc_chip.clone(), + layouter.namespace(|| "cm_old"), + circuit.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"), + circuit.g_d_old.as_ref().map(|gd| gd.to_affine()), + )?; + + // Witness ak_P. + let ak_P: Value = circuit.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], + circuit.nk.map(|nk| nk.inner()), + )?; + + // Witness v_old. + let v_old = assign_free_advice( + layouter.namespace(|| "witness v_old"), + config.advices[0], + circuit.v_old, + )?; + + // Witness v_new. + let v_new = assign_free_advice( + layouter.namespace(|| "witness v_new"), + config.advices[0], + circuit.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 = circuit + .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, + circuit.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 = circuit.v_old - circuit.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"), + circuit.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"), + circuit.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"), + circuit.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"), + circuit + .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"), + circuit.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(), + 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)?; + } + + // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). + { + // Witness g_d_new + let g_d_new = { + let g_d_new = circuit.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 = circuit + .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 = 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], + circuit.psi_new, + )?; + + let rcm_new = ScalarFixed::new( + ecc_chip, + layouter.namespace(|| "rcm_new"), + circuit.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(), + 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 spends", + config.primary, + ENABLE_SPEND, + config.advices[6], + 0, + )?; + + region.assign_advice_from_instance( + || "enable outputs", + config.primary, + ENABLE_OUTPUT, + config.advices[7], + 0, + )?; + + config.q_orchard.enable(&mut region, 0) + }, + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use core::iter; + + use ff::Field; + use halo2_proofs::{circuit::Value, dev::MockProver}; + use pasta_curves::pallas; + use rand::{rngs::OsRng, RngCore}; + + use crate::{ + bundle::Flags, + circuit::{CircuitBase, Instance, Proof, ProvingKey, VerifyingKey, K}, + keys::SpendValidatingKey, + note::{AssetBase, Note}, + orchard_flavors::OrchardVanilla, + tree::MerklePath, + value::{ValueCommitTrapdoor, ValueCommitment}, + }; + + type OrchardCircuitVanilla = CircuitBase; + + fn generate_circuit_instance(mut rng: R) -> (OrchardCircuitVanilla, Instance) { + let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetBase::native()); + + 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 ak: SpendValidatingKey = fvk.into(); + let alpha = pallas::Scalar::random(&mut rng); + let rk = ak.randomize(&alpha); + + let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetBase::native()); + 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, AssetBase::native()); + + let path = MerklePath::dummy(&mut rng); + let anchor = path.root(spent_note.commitment().into()); + + let psi_old = spent_note.rseed().psi(&spent_note.rho()); + + ( + OrchardCircuitVanilla { + 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.rseed().psi(&spent_note.rho())), + rcm_old: Value::known(spent_note.rseed().rcm(&spent_note.rho())), + cm_old: Value::known(spent_note.commitment()), + // For non split note, psi_nf is equal to psi_old + psi_nf: Value::known(psi_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.rseed().psi(&output_note.rho())), + rcm_new: Value::known(output_note.rseed().rcm(&output_note.rho())), + rcv: Value::known(rcv), + asset: Value::known(spent_note.asset()), + split_flag: Value::known(false), + phantom: std::marker::PhantomData, + }, + Instance { + anchor, + cv_net, + nf_old, + rk, + cmx, + enable_spend: true, + enable_output: true, + enable_zsa: false, + }, + ) + } + + // TODO: recast as a proptest + #[test] + fn round_trip() { + let mut rng = OsRng; + + let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) + .map(|()| generate_circuit_instance(&mut rng)) + .unzip(); + + let vk = VerifyingKey::build::(); + + // Test that the pinned verification key (representing the circuit) + // is as expected. + { + // panic!("{:#?}", vk.vk.pinned()); + assert_eq!( + format!("{:#?}\n", vk.vk.pinned()), + include_str!("circuit_description_vanilla").replace("\r\n", "\n") + ); + } + + // Test that the proof size is as expected. + let expected_proof_size = { + let circuit_cost = + halo2_proofs::dev::CircuitCost::::measure( + K, + &circuits[0], + ); + println!("{:#?}", circuit_cost); + assert_eq!(usize::from(circuit_cost.proof_size(1)), 4992); + assert_eq!(usize::from(circuit_cost.proof_size(2)), 7264); + 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::(); + 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 serialized_proof_test_case() { + use std::io::{Read, Write}; + + let vk = VerifyingKey::build::(); + + fn write_test_case( + mut w: W, + instance: &Instance, + proof: &Proof, + ) -> 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.clone()))?; + w.write_all(&instance.cmx.to_bytes())?; + w.write_all(&[ + u8::from(instance.enable_spend), + u8::from(instance.enable_output), + ])?; + + w.write_all(proof.as_ref())?; + Ok(()) + } + + fn read_test_case(mut r: R) -> 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 enable_zsa = false; + let instance = Instance::from_parts( + anchor, + cv_net, + nf_old, + rk, + cmx, + Flags::from_parts(enable_spend, enable_output, enable_zsa), + ); + + let mut proof_bytes = vec![]; + r.read_to_end(&mut proof_bytes)?; + let proof = Proof::new(proof_bytes); + + Ok((instance, proof)) + } + + 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, instance) = generate_circuit_instance(OsRng); + let instances = &[instance.clone()]; + + let pk = ProvingKey::build::(); + let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); + assert!(proof.verify(&vk, instances).is_ok()); + + let file = std::fs::File::create("circuit_proof_test_case.bin")?; + write_test_case(file, &instance, &proof) + }; + create_proof().expect("should be able to write new proof"); + } + + // Parse the hardcoded proof test case. + let (instance, proof) = { + let test_case_bytes = include_bytes!("circuit_proof_test_case_vanilla.bin"); + read_test_case(&test_case_bytes[..]).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 = OrchardCircuitVanilla { + 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(), + psi_nf: 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(), + asset: Value::unknown(), + split_flag: Value::unknown(), + phantom: std::marker::PhantomData, + }; + halo2_proofs::dev::CircuitLayout::default() + .show_labels(false) + .view_height(0..(1 << 11)) + .render(K, &circuit, &root) + .unwrap(); + } +} diff --git a/src/circuit/circuit_vanilla/gadget.rs b/src/circuit/circuit_vanilla/gadget.rs new file mode 100644 index 000000000..f78b7c0ff --- /dev/null +++ b/src/circuit/circuit_vanilla/gadget.rs @@ -0,0 +1,192 @@ +//! Gadgets used in the Orchard circuit. + +// TODO: try to avoid code duplication with the same module for circuit_zsa. + +use ff::Field; +use pasta_curves::pallas; + +use super::{add_chip, commit_ivk::CommitIvkChip, note_commit::NoteCommitChip, AddInstruction}; +use crate::constants::{ + NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains, + ValueCommitV, +}; +use halo2_gadgets::{ + ecc::{ + chip::EccChip, EccInstructions, FixedPoint, FixedPointBaseField, FixedPointShort, Point, + ScalarFixed, ScalarFixedShort, X, + }, + poseidon::{ + primitives::{self as poseidon, ConstantLength}, + Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip, + }, + sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}, +}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + plonk::{self, Advice, Assigned, Column}, +}; + +impl super::Config { + pub(super) fn add_chip(&self) -> add_chip::AddChip { + add_chip::AddChip::construct(self.add_config.clone()) + } + + pub(super) fn commit_ivk_chip(&self) -> CommitIvkChip { + CommitIvkChip::construct(self.commit_ivk_config.clone()) + } + + pub(super) fn ecc_chip(&self) -> EccChip { + EccChip::construct(self.ecc_config.clone()) + } + + pub(super) fn sinsemilla_chip_1( + &self, + ) -> SinsemillaChip { + SinsemillaChip::construct(self.sinsemilla_config_1.clone()) + } + + pub(super) fn sinsemilla_chip_2( + &self, + ) -> SinsemillaChip { + SinsemillaChip::construct(self.sinsemilla_config_2.clone()) + } + + pub(super) fn merkle_chip_1( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_1.clone()) + } + + pub(super) fn merkle_chip_2( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_2.clone()) + } + + pub(super) fn poseidon_chip(&self) -> PoseidonChip { + PoseidonChip::construct(self.poseidon_config.clone()) + } + + 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 { + NoteCommitChip::construct(self.old_note_commit_config.clone()) + } +} + +/// Witnesses the given value in a standalone region. +/// +/// Usages of this helper are technically superfluous, as the single-cell region is only +/// ever used in equality constraints. We could eliminate them with a +/// [write-on-copy abstraction](https://github.com/zcash/halo2/issues/334). +pub(in crate::circuit) fn assign_free_advice( + mut layouter: impl Layouter, + column: Column, + value: Value, +) -> Result, plonk::Error> +where + for<'v> Assigned: From<&'v V>, +{ + layouter.assign_region( + || "load private", + |mut region| region.assign_advice(|| "load private", column, 0, || value), + ) +} + +/// `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 = ValueCommitV; + let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v); + 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) +} + +/// `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)] +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); + 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()) +} + +pub(in crate::circuit) use super::commit_ivk::gadgets::commit_ivk; +pub(in crate::circuit) use super::note_commit::gadgets::note_commit; diff --git a/src/circuit/circuit_vanilla/note_commit.rs b/src/circuit/circuit_vanilla/note_commit.rs new file mode 100644 index 000000000..922ad6356 --- /dev/null +++ b/src/circuit/circuit_vanilla/note_commit.rs @@ -0,0 +1,2364 @@ +// TODO: try to avoid code duplication with the same module for circuit_zsa. +// +use core::iter; + +use group::ff::PrimeField; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector}, + poly::Rotation, +}; +use pasta_curves::pallas; + +use crate::{ + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P}, + value::NoteValue, +}; +use halo2_gadgets::{ + ecc::{ + chip::{EccChip, NonIdentityEccPoint}, + Point, ScalarFixed, + }, + sinsemilla::{ + chip::{SinsemillaChip, SinsemillaConfig}, + CommitDomain, Message, MessagePiece, + }, + utilities::{ + bool_check, lookup_range_check::LookupRangeCheckConfig, FieldValue, RangeConstrained, + }, +}; + +type NoteCommitPiece = MessagePiece< + pallas::Affine, + SinsemillaChip, + 10, + 253, +>; + +/// The values of the running sum at the start and end of the range being used for a +/// canonicity check. +type CanonicityBounds = ( + AssignedCell, + AssignedCell, +); + +/* + + We need to hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi, + where + - g★_d is the representation of the point g_d, with 255 bits used for the + x-coordinate and 1 bit used for the y-coordinate; + - pk★_d is the representation of the point pk_d, with 255 bits used for the + x-coordinate and 1 bit used for the y-coordinate; + - v is a 64-bit value; + - rho is a base field element (255 bits); and + - psi is a base field element (255 bits). +*/ + +/// b = b_0 || b_1 || b_2 || b_3 +/// = (bits 250..=253 of x(g_d)) || (bit 254 of x(g_d)) || (ỹ bit of g_d) || (bits 0..=3 of pk★_d) +/// +/// | A_6 | A_7 | A_8 | q_notecommit_b | +/// ------------------------------------ +/// | b | b_0 | b_1 | 1 | +/// | | b_2 | b_3 | 0 | +/// +/// +#[derive(Clone, Debug)] +struct DecomposeB { + q_notecommit_b: Selector, + col_l: Column, + col_m: Column, + col_r: Column, +} + +impl DecomposeB { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two_pow_4: pallas::Base, + two_pow_5: pallas::Base, + two_pow_6: pallas::Base, + ) -> Self { + let q_notecommit_b = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece b", |meta| { + let q_notecommit_b = meta.query_selector(q_notecommit_b); + + // b has been constrained to 10 bits by the Sinsemilla hash. + let b = meta.query_advice(col_l, Rotation::cur()); + // b_0 has been constrained to be 4 bits outside this gate. + let b_0 = meta.query_advice(col_m, Rotation::cur()); + // This gate constrains b_1 to be boolean. + let b_1 = meta.query_advice(col_r, Rotation::cur()); + // This gate constrains b_2 to be boolean. + let b_2 = meta.query_advice(col_m, Rotation::next()); + // b_3 has been constrained to 4 bits outside this gate. + let b_3 = meta.query_advice(col_r, Rotation::next()); + + // b = b_0 + (2^4) b_1 + (2^5) b_2 + (2^6) b_3 + let decomposition_check = + b - (b_0 + b_1.clone() * two_pow_4 + b_2.clone() * two_pow_5 + b_3 * two_pow_6); + + Constraints::with_selector( + q_notecommit_b, + [ + ("bool_check b_1", bool_check(b_1)), + ("bool_check b_2", bool_check(b_2)), + ("decomposition", decomposition_check), + ], + ) + }); + + Self { + q_notecommit_b, + col_l, + col_m, + col_r, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &LookupRangeCheckConfig, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + g_d: &NonIdentityEccPoint, + pk_d: &NonIdentityEccPoint, + ) -> Result< + ( + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + let (gd_x, gd_y) = (g_d.x(), g_d.y()); + + // Constrain b_0 to be 4 bits + let b_0 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "b_0"), + gd_x.value(), + 250..254, + )?; + + // b_1, b_2 will be boolean-constrained in the gate. + let b_1 = RangeConstrained::bitrange_of(gd_x.value(), 254..255); + let b_2 = RangeConstrained::bitrange_of(gd_y.value(), 0..1); + + // Constrain b_3 to be 4 bits + let b_3 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "b_3"), + pk_d.x().value(), + 0..4, + )?; + + let b = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "b"), + [b_0.value(), b_1, b_2, b_3.value()], + )?; + + Ok((b, b_0, b_1, b_2, b_3)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + b: NoteCommitPiece, + b_0: RangeConstrained>, + b_1: RangeConstrained>, + b_2: RangeConstrained>, + b_3: RangeConstrained>, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece b", + |mut region| { + self.q_notecommit_b.enable(&mut region, 0)?; + + b.inner() + .cell_value() + .copy_advice(|| "b", &mut region, self.col_l, 0)?; + b_0.inner() + .copy_advice(|| "b_0", &mut region, self.col_m, 0)?; + let b_1 = region.assign_advice(|| "b_1", self.col_r, 0, || *b_1.inner())?; + + b_2.inner() + .copy_advice(|| "b_2", &mut region, self.col_m, 1)?; + b_3.inner() + .copy_advice(|| "b_3", &mut region, self.col_r, 1)?; + + Ok(b_1) + }, + ) + } +} + +/// d = d_0 || d_1 || d_2 || d_3 +/// = (bit 254 of x(pk_d)) || (ỹ bit of pk_d) || (bits 0..=7 of v) || (bits 8..=57 of v) +/// +/// | A_6 | A_7 | A_8 | q_notecommit_d | +/// ------------------------------------ +/// | d | d_0 | d_1 | 1 | +/// | | d_2 | d_3 | 0 | +/// +/// +#[derive(Clone, Debug)] +struct DecomposeD { + q_notecommit_d: Selector, + col_l: Column, + col_m: Column, + col_r: Column, +} + +impl DecomposeD { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two: pallas::Base, + two_pow_2: pallas::Base, + two_pow_10: pallas::Base, + ) -> Self { + let q_notecommit_d = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece d", |meta| { + let q_notecommit_d = meta.query_selector(q_notecommit_d); + + // d has been constrained to 60 bits by the Sinsemilla hash. + let d = meta.query_advice(col_l, Rotation::cur()); + // This gate constrains d_0 to be boolean. + let d_0 = meta.query_advice(col_m, Rotation::cur()); + // This gate constrains d_1 to be boolean. + let d_1 = meta.query_advice(col_r, Rotation::cur()); + // d_2 has been constrained to 8 bits outside this gate. + let d_2 = meta.query_advice(col_m, Rotation::next()); + // d_3 is set to z1_d. + let d_3 = meta.query_advice(col_r, Rotation::next()); + + // d = d_0 + (2) d_1 + (2^2) d_2 + (2^10) d_3 + let decomposition_check = + d - (d_0.clone() + d_1.clone() * two + d_2 * two_pow_2 + d_3 * two_pow_10); + + Constraints::with_selector( + q_notecommit_d, + [ + ("bool_check d_0", bool_check(d_0)), + ("bool_check d_1", bool_check(d_1)), + ("decomposition", decomposition_check), + ], + ) + }); + + Self { + q_notecommit_d, + col_l, + col_m, + col_r, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &LookupRangeCheckConfig, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + pk_d: &NonIdentityEccPoint, + value: &AssignedCell, + ) -> Result< + ( + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + let value_val = value.value().map(|v| pallas::Base::from(v.inner())); + + // d_0, d_1 will be boolean-constrained in the gate. + let d_0 = RangeConstrained::bitrange_of(pk_d.x().value(), 254..255); + let d_1 = RangeConstrained::bitrange_of(pk_d.y().value(), 0..1); + + // Constrain d_2 to be 8 bits + let d_2 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "d_2"), + value_val.as_ref(), + 0..8, + )?; + + // d_3 = z1_d from the SinsemillaHash(d) running sum output. + let d_3 = RangeConstrained::bitrange_of(value_val.as_ref(), 8..58); + + let d = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "d"), + [d_0, d_1, d_2.value(), d_3], + )?; + + Ok((d, d_0, d_1, d_2)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + d: NoteCommitPiece, + d_0: RangeConstrained>, + d_1: RangeConstrained>, + d_2: RangeConstrained>, + z1_d: AssignedCell, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece d", + |mut region| { + self.q_notecommit_d.enable(&mut region, 0)?; + + d.inner() + .cell_value() + .copy_advice(|| "d", &mut region, self.col_l, 0)?; + let d_0 = region.assign_advice(|| "d_0", self.col_m, 0, || *d_0.inner())?; + d_1.inner() + .copy_advice(|| "d_1", &mut region, self.col_r, 0)?; + + d_2.inner() + .copy_advice(|| "d_2", &mut region, self.col_m, 1)?; + z1_d.copy_advice(|| "d_3 = z1_d", &mut region, self.col_r, 1)?; + + Ok(d_0) + }, + ) + } +} + +/// e = e_0 || e_1 = (bits 58..=63 of v) || (bits 0..=3 of rho) +/// +/// | A_6 | A_7 | A_8 | q_notecommit_e | +/// ------------------------------------ +/// | e | e_0 | e_1 | 1 | +/// +/// +#[derive(Clone, Debug)] +struct DecomposeE { + q_notecommit_e: Selector, + col_l: Column, + col_m: Column, + col_r: Column, +} + +impl DecomposeE { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two_pow_6: pallas::Base, + ) -> Self { + let q_notecommit_e = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece e", |meta| { + let q_notecommit_e = meta.query_selector(q_notecommit_e); + + // e has been constrained to 10 bits by the Sinsemilla hash. + let e = meta.query_advice(col_l, Rotation::cur()); + // e_0 has been constrained to 6 bits outside this gate. + let e_0 = meta.query_advice(col_m, Rotation::cur()); + // e_1 has been constrained to 4 bits outside this gate. + let e_1 = meta.query_advice(col_r, Rotation::cur()); + + // e = e_0 + (2^6) e_1 + let decomposition_check = e - (e_0 + e_1 * two_pow_6); + + Constraints::with_selector(q_notecommit_e, Some(("decomposition", decomposition_check))) + }); + + Self { + q_notecommit_e, + col_l, + col_m, + col_r, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &LookupRangeCheckConfig, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + value: &AssignedCell, + rho: &AssignedCell, + ) -> Result< + ( + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + let value_val = value.value().map(|v| pallas::Base::from(v.inner())); + + // Constrain e_0 to be 6 bits. + let e_0 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "e_0"), + value_val.as_ref(), + 58..64, + )?; + + // Constrain e_1 to be 4 bits. + let e_1 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "e_1"), + rho.value(), + 0..4, + )?; + + let e = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "e"), + [e_0.value(), e_1.value()], + )?; + + Ok((e, e_0, e_1)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + e: NoteCommitPiece, + e_0: RangeConstrained>, + e_1: RangeConstrained>, + ) -> Result<(), Error> { + layouter.assign_region( + || "NoteCommit MessagePiece e", + |mut region| { + self.q_notecommit_e.enable(&mut region, 0)?; + + e.inner() + .cell_value() + .copy_advice(|| "e", &mut region, self.col_l, 0)?; + e_0.inner() + .copy_advice(|| "e_0", &mut region, self.col_m, 0)?; + e_1.inner() + .copy_advice(|| "e_1", &mut region, self.col_r, 0)?; + + Ok(()) + }, + ) + } +} + +/// g = g_0 || g_1 || g_2 +/// = (bit 254 of rho) || (bits 0..=8 of psi) || (bits 9..=248 of psi) +/// +/// | A_6 | A_7 | q_notecommit_g | +/// ------------------------------ +/// | g | g_0 | 1 | +/// | g_1 | g_2 | 0 | +/// +/// +#[derive(Clone, Debug)] +struct DecomposeG { + q_notecommit_g: Selector, + col_l: Column, + col_m: Column, +} + +impl DecomposeG { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + two: pallas::Base, + two_pow_10: pallas::Base, + ) -> Self { + let q_notecommit_g = meta.selector(); + + meta.create_gate("NoteCommit MessagePiece g", |meta| { + let q_notecommit_g = meta.query_selector(q_notecommit_g); + + // g has been constrained to 250 bits by the Sinsemilla hash. + let g = meta.query_advice(col_l, Rotation::cur()); + // This gate constrains g_0 to be boolean. + let g_0 = meta.query_advice(col_m, Rotation::cur()); + // g_1 has been constrained to 9 bits outside this gate. + let g_1 = meta.query_advice(col_l, Rotation::next()); + // g_2 is set to z1_g. + let g_2 = meta.query_advice(col_m, Rotation::next()); + + // g = g_0 + (2) g_1 + (2^10) g_2 + let decomposition_check = g - (g_0.clone() + g_1 * two + g_2 * two_pow_10); + + Constraints::with_selector( + q_notecommit_g, + [ + ("bool_check g_0", bool_check(g_0)), + ("decomposition", decomposition_check), + ], + ) + }); + + Self { + q_notecommit_g, + col_l, + col_m, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &LookupRangeCheckConfig, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + rho: &AssignedCell, + psi: &AssignedCell, + ) -> Result< + ( + NoteCommitPiece, + RangeConstrained>, + RangeConstrained>, + ), + Error, + > { + // g_0 will be boolean-constrained in the gate. + let g_0 = RangeConstrained::bitrange_of(rho.value(), 254..255); + + // Constrain g_1 to be 9 bits. + let g_1 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "g_1"), + psi.value(), + 0..9, + )?; + + // g_2 = z1_g from the SinsemillaHash(g) running sum output. + let g_2 = RangeConstrained::bitrange_of(psi.value(), 9..249); + + let g = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "g"), + [g_0, g_1.value(), g_2], + )?; + + Ok((g, g_0, g_1)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + g: NoteCommitPiece, + g_0: RangeConstrained>, + g_1: RangeConstrained>, + z1_g: AssignedCell, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece g", + |mut region| { + self.q_notecommit_g.enable(&mut region, 0)?; + + g.inner() + .cell_value() + .copy_advice(|| "g", &mut region, self.col_l, 0)?; + let g_0 = region.assign_advice(|| "g_0", self.col_m, 0, || *g_0.inner())?; + + g_1.inner() + .copy_advice(|| "g_1", &mut region, self.col_l, 1)?; + z1_g.copy_advice(|| "g_2 = z1_g", &mut region, self.col_m, 1)?; + + Ok(g_0) + }, + ) + } +} + +/// h = h_0 || h_1 || h_2 +/// = (bits 249..=253 of psi) || (bit 254 of psi) || 4 zero bits +/// +/// | A_6 | A_7 | A_8 | q_notecommit_h | +/// ------------------------------------ +/// | h | h_0 | h_1 | 1 | +/// +/// +#[derive(Clone, Debug)] +struct DecomposeH { + q_notecommit_h: Selector, + col_l: Column, + col_m: Column, + col_r: Column, +} + +impl DecomposeH { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + two_pow_5: 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 has been constrained to 10 bits by the Sinsemilla hash. + let h = 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 = h_0 + (2^5) h_1 + let decomposition_check = h - (h_0 + h_1.clone() * two_pow_5); + + Constraints::with_selector( + q_notecommit_h, + [ + ("bool_check h_1", bool_check(h_1)), + ("decomposition", decomposition_check), + ], + ) + }); + + Self { + q_notecommit_h, + col_l, + col_m, + col_r, + } + } + + #[allow(clippy::type_complexity)] + fn decompose( + lookup_config: &LookupRangeCheckConfig, + chip: SinsemillaChip, + layouter: &mut impl Layouter, + psi: &AssignedCell, + ) -> Result< + ( + NoteCommitPiece, + 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); + + let h = MessagePiece::from_subpieces( + chip, + layouter.namespace(|| "h"), + [ + h_0.value(), + h_1, + RangeConstrained::bitrange_of(Value::known(&pallas::Base::zero()), 0..4), + ], + )?; + + Ok((h, h_0, h_1)) + } + + fn assign( + &self, + layouter: &mut impl Layouter, + h: NoteCommitPiece, + h_0: RangeConstrained>, + h_1: RangeConstrained>, + ) -> Result, Error> { + layouter.assign_region( + || "NoteCommit MessagePiece h", + |mut region| { + self.q_notecommit_h.enable(&mut region, 0)?; + + h.inner() + .cell_value() + .copy_advice(|| "h", &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())?; + + Ok(h_1) + }, + ) + } +} + +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_g_d | +/// ----------------------------------------------------------- +/// | x(g_d) | b_0 | a | z13_a | 1 | +/// | | b_1 | a_prime | z13_a_prime | 0 | +/// +/// +#[derive(Clone, Debug)] +struct GdCanonicity { + q_notecommit_g_d: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, +} + +impl GdCanonicity { + #[allow(clippy::too_many_arguments)] + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, + two_pow_130: Expression, + two_pow_250: pallas::Base, + two_pow_254: pallas::Base, + t_p: Expression, + ) -> Self { + let q_notecommit_g_d = meta.selector(); + + meta.create_gate("NoteCommit input g_d", |meta| { + let q_notecommit_g_d = meta.query_selector(q_notecommit_g_d); + + let gd_x = meta.query_advice(col_l, Rotation::cur()); + + // b_0 has been constrained to be 4 bits outside this gate. + let b_0 = meta.query_advice(col_m, Rotation::cur()); + // b_1 has been constrained to be boolean outside this gate. + let b_1 = meta.query_advice(col_m, Rotation::next()); + + // a has been constrained to 250 bits by the Sinsemilla hash. + let a = meta.query_advice(col_r, Rotation::cur()); + let a_prime = meta.query_advice(col_r, Rotation::next()); + + let z13_a = meta.query_advice(col_z, Rotation::cur()); + let z13_a_prime = meta.query_advice(col_z, Rotation::next()); + + // x(g_d) = a + (2^250)b_0 + (2^254)b_1 + let decomposition_check = { + let sum = a.clone() + b_0.clone() * two_pow_250 + b_1.clone() * two_pow_254; + sum - gd_x + }; + + // a_prime = a + 2^130 - t_P + let a_prime_check = a + two_pow_130 - t_p - a_prime; + + // The gd_x_canonicity_checks are enforced if and only if `b_1` = 1. + // x(g_d) = a (250 bits) || b_0 (4 bits) || b_1 (1 bit) + let canonicity_checks = iter::empty() + .chain(Some(("b_1 = 1 => b_0", b_0))) + .chain(Some(("b_1 = 1 => z13_a", z13_a))) + .chain(Some(("b_1 = 1 => z13_a_prime", z13_a_prime))) + .map(move |(name, poly)| (name, b_1.clone() * poly)); + + Constraints::with_selector( + q_notecommit_g_d, + iter::empty() + .chain(Some(("decomposition", decomposition_check))) + .chain(Some(("a_prime_check", a_prime_check))) + .chain(canonicity_checks), + ) + }); + + Self { + q_notecommit_g_d, + col_l, + col_m, + col_r, + col_z, + } + } + + #[allow(clippy::too_many_arguments)] + fn assign( + &self, + layouter: &mut impl Layouter, + g_d: &NonIdentityEccPoint, + a: NoteCommitPiece, + b_0: RangeConstrained>, + b_1: AssignedCell, + a_prime: AssignedCell, + z13_a: AssignedCell, + z13_a_prime: AssignedCell, + ) -> Result<(), Error> { + layouter.assign_region( + || "NoteCommit input g_d", + |mut region| { + g_d.x().copy_advice(|| "gd_x", &mut region, self.col_l, 0)?; + + b_0.inner() + .copy_advice(|| "b_0", &mut region, self.col_m, 0)?; + b_1.copy_advice(|| "b_1", &mut region, self.col_m, 1)?; + + a.inner() + .cell_value() + .copy_advice(|| "a", &mut region, self.col_r, 0)?; + a_prime.copy_advice(|| "a_prime", &mut region, self.col_r, 1)?; + + z13_a.copy_advice(|| "z13_a", &mut region, self.col_z, 0)?; + z13_a_prime.copy_advice(|| "z13_a_prime", &mut region, self.col_z, 1)?; + + self.q_notecommit_g_d.enable(&mut region, 0) + }, + ) + } +} + +/// | 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 | +/// +/// +#[derive(Clone, Debug)] +struct PkdCanonicity { + q_notecommit_pk_d: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, +} + +impl PkdCanonicity { + #[allow(clippy::too_many_arguments)] + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, + two_pow_4: pallas::Base, + two_pow_140: Expression, + two_pow_254: pallas::Base, + t_p: Expression, + ) -> Self { + let q_notecommit_pk_d = meta.selector(); + + meta.create_gate("NoteCommit input pk_d", |meta| { + let q_notecommit_pk_d = meta.query_selector(q_notecommit_pk_d); + + let pkd_x = meta.query_advice(col_l, Rotation::cur()); + + // `b_3` has been constrained to 4 bits outside this gate. + let b_3 = meta.query_advice(col_m, Rotation::cur()); + // d_0 has been constrained to be boolean outside this gate. + let d_0 = meta.query_advice(col_m, Rotation::next()); + + // `c` has been constrained to 250 bits by the Sinsemilla hash. + let c = meta.query_advice(col_r, Rotation::cur()); + let b3_c_prime = meta.query_advice(col_r, Rotation::next()); + + let z13_c = meta.query_advice(col_z, Rotation::cur()); + let z14_b3_c_prime = meta.query_advice(col_z, Rotation::next()); + + // x(pk_d) = b_3 + (2^4)c + (2^254)d_0 + let decomposition_check = { + let sum = b_3.clone() + c.clone() * two_pow_4 + d_0.clone() * two_pow_254; + sum - pkd_x + }; + + // b3_c_prime = b_3 + (2^4)c + 2^140 - t_P + let b3_c_prime_check = b_3 + (c * two_pow_4) + two_pow_140 - t_p - b3_c_prime; + + // The pkd_x_canonicity_checks are enforced if and only if `d_0` = 1. + // `x(pk_d)` = `b_3 (4 bits) || c (250 bits) || d_0 (1 bit)` + let canonicity_checks = iter::empty() + .chain(Some(("d_0 = 1 => z13_c", z13_c))) + .chain(Some(("d_0 = 1 => z14_b3_c_prime", z14_b3_c_prime))) + .map(move |(name, poly)| (name, d_0.clone() * poly)); + + Constraints::with_selector( + q_notecommit_pk_d, + iter::empty() + .chain(Some(("decomposition", decomposition_check))) + .chain(Some(("b3_c_prime_check", b3_c_prime_check))) + .chain(canonicity_checks), + ) + }); + + Self { + q_notecommit_pk_d, + col_l, + col_m, + col_r, + col_z, + } + } + + #[allow(clippy::too_many_arguments)] + fn assign( + &self, + layouter: &mut impl Layouter, + pk_d: &NonIdentityEccPoint, + b_3: RangeConstrained>, + 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", + |mut region| { + pk_d.x() + .copy_advice(|| "pkd_x", &mut region, self.col_l, 0)?; + + b_3.inner() + .copy_advice(|| "b_3", &mut region, self.col_m, 0)?; + d_0.copy_advice(|| "d_0", &mut region, self.col_m, 1)?; + + c.inner() + .cell_value() + .copy_advice(|| "c", &mut region, self.col_r, 0)?; + b3_c_prime.copy_advice(|| "b3_c_prime", &mut region, self.col_r, 1)?; + + 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) + }, + ) + } +} + +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_value | +/// ------------------------------------------------ +/// | value | d_2 | d_3 | e_0 | 1 | +/// +/// +#[derive(Clone, Debug)] +struct ValueCanonicity { + q_notecommit_value: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, +} + +impl ValueCanonicity { + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, + two_pow_8: pallas::Base, + two_pow_58: pallas::Base, + ) -> Self { + let q_notecommit_value = meta.selector(); + + meta.create_gate("NoteCommit input value", |meta| { + let q_notecommit_value = meta.query_selector(q_notecommit_value); + + let value = meta.query_advice(col_l, Rotation::cur()); + // d_2 has been constrained to 8 bits outside this gate. + let d_2 = meta.query_advice(col_m, Rotation::cur()); + // z1_d has been constrained to 50 bits by the Sinsemilla hash. + let z1_d = meta.query_advice(col_r, Rotation::cur()); + let d_3 = z1_d; + // `e_0` has been constrained to 6 bits outside this gate. + let e_0 = meta.query_advice(col_z, Rotation::cur()); + + // value = d_2 + (2^8)d_3 + (2^58)e_0 + let value_check = d_2 + d_3 * two_pow_8 + e_0 * two_pow_58 - value; + + Constraints::with_selector(q_notecommit_value, Some(("value_check", value_check))) + }); + + Self { + q_notecommit_value, + col_l, + col_m, + col_r, + col_z, + } + } + + fn assign( + &self, + layouter: &mut impl Layouter, + value: AssignedCell, + d_2: RangeConstrained>, + z1_d: AssignedCell, + e_0: RangeConstrained>, + ) -> Result<(), Error> { + layouter.assign_region( + || "NoteCommit input value", + |mut region| { + value.copy_advice(|| "value", &mut region, self.col_l, 0)?; + d_2.inner() + .copy_advice(|| "d_2", &mut region, self.col_m, 0)?; + z1_d.copy_advice(|| "d3 = z1_d", &mut region, self.col_r, 0)?; + e_0.inner() + .copy_advice(|| "e_0", &mut region, self.col_z, 0)?; + + self.q_notecommit_value.enable(&mut region, 0) + }, + ) + } +} + +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_rho | +/// -------------------------------------------------------------- +/// | rho | e_1 | f | z13_f | 1 | +/// | | g_0 | e1_f_prime | z14_e1_f_prime | 0 | +/// +/// +#[derive(Clone, Debug)] +struct RhoCanonicity { + q_notecommit_rho: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, +} + +impl RhoCanonicity { + #[allow(clippy::too_many_arguments)] + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, + two_pow_4: pallas::Base, + two_pow_140: Expression, + two_pow_254: pallas::Base, + t_p: Expression, + ) -> Self { + let q_notecommit_rho = meta.selector(); + + meta.create_gate("NoteCommit input rho", |meta| { + let q_notecommit_rho = meta.query_selector(q_notecommit_rho); + + let rho = meta.query_advice(col_l, Rotation::cur()); + + // `e_1` has been constrained to 4 bits outside this gate. + let e_1 = meta.query_advice(col_m, Rotation::cur()); + let g_0 = meta.query_advice(col_m, Rotation::next()); + + // `f` has been constrained to 250 bits by the Sinsemilla hash. + let f = meta.query_advice(col_r, Rotation::cur()); + let e1_f_prime = meta.query_advice(col_r, Rotation::next()); + + let z13_f = meta.query_advice(col_z, Rotation::cur()); + let z14_e1_f_prime = meta.query_advice(col_z, Rotation::next()); + + // rho = e_1 + (2^4) f + (2^254) g_0 + let decomposition_check = { + let sum = e_1.clone() + f.clone() * two_pow_4 + g_0.clone() * two_pow_254; + sum - rho + }; + + // e1_f_prime = e_1 + (2^4)f + 2^140 - t_P + let e1_f_prime_check = e_1 + (f * two_pow_4) + two_pow_140 - t_p - e1_f_prime; + + // The rho_canonicity_checks are enforced if and only if `g_0` = 1. + // rho = e_1 (4 bits) || f (250 bits) || g_0 (1 bit) + let canonicity_checks = iter::empty() + .chain(Some(("g_0 = 1 => z13_f", z13_f))) + .chain(Some(("g_0 = 1 => z14_e1_f_prime", z14_e1_f_prime))) + .map(move |(name, poly)| (name, g_0.clone() * poly)); + + Constraints::with_selector( + q_notecommit_rho, + iter::empty() + .chain(Some(("decomposition", decomposition_check))) + .chain(Some(("e1_f_prime_check", e1_f_prime_check))) + .chain(canonicity_checks), + ) + }); + + Self { + q_notecommit_rho, + col_l, + col_m, + col_r, + col_z, + } + } + + #[allow(clippy::too_many_arguments)] + fn assign( + &self, + layouter: &mut impl Layouter, + rho: AssignedCell, + e_1: RangeConstrained>, + f: NoteCommitPiece, + g_0: AssignedCell, + e1_f_prime: AssignedCell, + z13_f: AssignedCell, + z14_e1_f_prime: AssignedCell, + ) -> Result<(), Error> { + layouter.assign_region( + || "NoteCommit input rho", + |mut region| { + rho.copy_advice(|| "rho", &mut region, self.col_l, 0)?; + + e_1.inner() + .copy_advice(|| "e_1", &mut region, self.col_m, 0)?; + g_0.copy_advice(|| "g_0", &mut region, self.col_m, 1)?; + + f.inner() + .cell_value() + .copy_advice(|| "f", &mut region, self.col_r, 0)?; + e1_f_prime.copy_advice(|| "e1_f_prime", &mut region, self.col_r, 1)?; + + z13_f.copy_advice(|| "z13_f", &mut region, self.col_z, 0)?; + z14_e1_f_prime.copy_advice(|| "z14_e1_f_prime", &mut region, self.col_z, 1)?; + + self.q_notecommit_rho.enable(&mut region, 0) + }, + ) + } +} + +/// | A_6 | A_7 | A_8 | A_9 | q_notecommit_psi | +/// ---------------------------------------------------------------- +/// | psi | g_1 | g_2 | z13_g | 1 | +/// | h_0 | h_1 | g1_g2_prime | z13_g1_g2_prime | 0 | +/// +/// +#[derive(Clone, Debug)] +struct PsiCanonicity { + q_notecommit_psi: Selector, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, +} + +impl PsiCanonicity { + #[allow(clippy::too_many_arguments)] + fn configure( + meta: &mut ConstraintSystem, + col_l: Column, + col_m: Column, + col_r: Column, + col_z: Column, + two_pow_9: pallas::Base, + two_pow_130: Expression, + two_pow_249: pallas::Base, + two_pow_254: pallas::Base, + t_p: Expression, + ) -> Self { + let q_notecommit_psi = meta.selector(); + + meta.create_gate("NoteCommit input psi", |meta| { + let q_notecommit_psi = meta.query_selector(q_notecommit_psi); + + let psi = meta.query_advice(col_l, Rotation::cur()); + let h_0 = meta.query_advice(col_l, Rotation::next()); + + let g_1 = meta.query_advice(col_m, Rotation::cur()); + let h_1 = meta.query_advice(col_m, Rotation::next()); + + let z1_g = meta.query_advice(col_r, Rotation::cur()); + let g_2 = z1_g; + let g1_g2_prime = meta.query_advice(col_r, Rotation::next()); + + let z13_g = meta.query_advice(col_z, Rotation::cur()); + let z13_g1_g2_prime = meta.query_advice(col_z, Rotation::next()); + + // psi = g_1 + (2^9) g_2 + (2^249) h_0 + (2^254) h_1 + let decomposition_check = { + let sum = g_1.clone() + + g_2.clone() * two_pow_9 + + h_0.clone() * two_pow_249 + + h_1.clone() * two_pow_254; + sum - psi + }; + + // g1_g2_prime = g_1 + (2^9)g_2 + 2^130 - t_P + let g1_g2_prime_check = g_1 + (g_2 * two_pow_9) + two_pow_130 - t_p - g1_g2_prime; + + // The psi_canonicity_checks are enforced if and only if `h_1` = 1. + // `psi` = `g_1 (9 bits) || g_2 (240 bits) || h_0 (5 bits) || h_1 (1 bit)` + let canonicity_checks = iter::empty() + .chain(Some(("h_1 = 1 => h_0", h_0))) + .chain(Some(("h_1 = 1 => z13_g", z13_g))) + .chain(Some(("h_1 = 1 => z13_g1_g2_prime", z13_g1_g2_prime))) + .map(move |(name, poly)| (name, h_1.clone() * poly)); + + Constraints::with_selector( + q_notecommit_psi, + iter::empty() + .chain(Some(("decomposition", decomposition_check))) + .chain(Some(("g1_g2_prime_check", g1_g2_prime_check))) + .chain(canonicity_checks), + ) + }); + + Self { + q_notecommit_psi, + col_l, + col_m, + col_r, + col_z, + } + } + + #[allow(clippy::too_many_arguments)] + fn assign( + &self, + layouter: &mut impl Layouter, + psi: AssignedCell, + g_1: RangeConstrained>, + z1_g: AssignedCell, + h_0: RangeConstrained>, + h_1: AssignedCell, + g1_g2_prime: AssignedCell, + z13_g: AssignedCell, + z13_g1_g2_prime: AssignedCell, + ) -> Result<(), Error> { + layouter.assign_region( + || "NoteCommit input psi", + |mut region| { + psi.copy_advice(|| "psi", &mut region, self.col_l, 0)?; + h_0.inner() + .copy_advice(|| "h_0", &mut region, self.col_l, 1)?; + + g_1.inner() + .copy_advice(|| "g_1", &mut region, self.col_m, 0)?; + h_1.copy_advice(|| "h_1", &mut region, self.col_m, 1)?; + + z1_g.copy_advice(|| "g_2 = z1_g", &mut region, self.col_r, 0)?; + g1_g2_prime.copy_advice(|| "g1_g2_prime", &mut region, self.col_r, 1)?; + + z13_g.copy_advice(|| "z13_g", &mut region, self.col_z, 0)?; + z13_g1_g2_prime.copy_advice(|| "z13_g1_g2_prime", &mut region, self.col_z, 1)?; + + self.q_notecommit_psi.enable(&mut region, 0) + }, + ) + } +} + +/// Check decomposition and canonicity of y-coordinates. +/// This is used for both y(g_d) and y(pk_d). +/// +/// y = LSB || k_0 || k_1 || k_2 || k_3 +/// = (bit 0) || (bits 1..=9) || (bits 10..=249) || (bits 250..=253) || (bit 254) +/// +/// These pieces are laid out in the following configuration: +/// | A_5 | A_6 | A_7 | A_8 | A_9 | q_y_canon | +/// --------------------------------------------------------- +/// | y | lsb | k_0 | k_2 | k_3 | 1 | +/// | j | z1_j| z13_j | j_prime | z13_j_prime | 0 | +/// where z1_j = k_1. +#[derive(Clone, Debug)] +struct YCanonicity { + q_y_canon: Selector, + advices: [Column; 10], +} + +impl YCanonicity { + #[allow(clippy::too_many_arguments)] + fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 10], + two: pallas::Base, + two_pow_10: pallas::Base, + two_pow_130: Expression, + two_pow_250: pallas::Base, + two_pow_254: pallas::Base, + t_p: Expression, + ) -> Self { + let q_y_canon = meta.selector(); + + meta.create_gate("y coordinate checks", |meta| { + let q_y_canon = meta.query_selector(q_y_canon); + let y = meta.query_advice(advices[5], Rotation::cur()); + // LSB has been boolean-constrained outside this gate. + let lsb = meta.query_advice(advices[6], Rotation::cur()); + // k_0 has been constrained to 9 bits outside this gate. + let k_0 = meta.query_advice(advices[7], Rotation::cur()); + // k_1 = z1_j (witnessed in the next rotation). + // k_2 has been constrained to 4 bits outside this gate. + let k_2 = meta.query_advice(advices[8], Rotation::cur()); + // This gate constrains k_3 to be boolean. + let k_3 = meta.query_advice(advices[9], Rotation::cur()); + + // j = LSB + (2)k_0 + (2^10)k_1 + let j = meta.query_advice(advices[5], Rotation::next()); + let z1_j = meta.query_advice(advices[6], Rotation::next()); + let z13_j = meta.query_advice(advices[7], Rotation::next()); + + // j_prime = j + 2^130 - t_P + let j_prime = meta.query_advice(advices[8], Rotation::next()); + let z13_j_prime = meta.query_advice(advices[9], Rotation::next()); + + // Decomposition checks + // https://p.z.cash/orchard-0.1:note-commit-decomposition-y?partial + let decomposition_checks = { + // Check that k_3 is boolean + let k3_check = bool_check(k_3.clone()); + // Check that j = LSB + (2)k_0 + (2^10)k_1 + let k_1 = z1_j; + let j_check = j.clone() - (lsb + k_0 * two + k_1 * two_pow_10); + // Check that y = j + (2^250)k_2 + (2^254)k_3 + let y_check = + y - (j.clone() + k_2.clone() * two_pow_250 + k_3.clone() * two_pow_254); + // Check that j_prime = j + 2^130 - t_P + let j_prime_check = j + two_pow_130 - t_p - j_prime; + + iter::empty() + .chain(Some(("k3_check", k3_check))) + .chain(Some(("j_check", j_check))) + .chain(Some(("y_check", y_check))) + .chain(Some(("j_prime_check", j_prime_check))) + }; + + // Canonicity checks. These are enforced if and only if k_3 = 1. + // https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial + let canonicity_checks = { + iter::empty() + .chain(Some(("k_3 = 1 => k_2 = 0", k_2))) + .chain(Some(("k_3 = 1 => z13_j = 0", z13_j))) + .chain(Some(("k_3 = 1 => z13_j_prime = 0", z13_j_prime))) + .map(move |(name, poly)| (name, k_3.clone() * poly)) + }; + + Constraints::with_selector(q_y_canon, decomposition_checks.chain(canonicity_checks)) + }); + + Self { q_y_canon, advices } + } + + #[allow(clippy::too_many_arguments)] + fn assign( + &self, + layouter: &mut impl Layouter, + y: AssignedCell, + lsb: RangeConstrained>, + k_0: RangeConstrained>, + k_2: RangeConstrained>, + k_3: RangeConstrained>, + j: AssignedCell, + z1_j: AssignedCell, + z13_j: AssignedCell, + j_prime: AssignedCell, + z13_j_prime: AssignedCell, + ) -> Result>, Error> + { + layouter.assign_region( + || "y canonicity", + |mut region| { + self.q_y_canon.enable(&mut region, 0)?; + + // Offset 0 + let lsb = { + let offset = 0; + + // Copy y. + y.copy_advice(|| "copy y", &mut region, self.advices[5], offset)?; + // Witness LSB. + let lsb = region + .assign_advice(|| "witness LSB", self.advices[6], offset, || *lsb.inner()) + // SAFETY: This is sound because we just assigned this cell from a + // range-constrained value. + .map(|cell| RangeConstrained::unsound_unchecked(cell, lsb.num_bits()))?; + // Witness k_0. + k_0.inner() + .copy_advice(|| "copy k_0", &mut region, self.advices[7], offset)?; + // Copy k_2. + k_2.inner() + .copy_advice(|| "copy k_2", &mut region, self.advices[8], offset)?; + // Witness k_3. + region.assign_advice( + || "witness k_3", + self.advices[9], + offset, + || *k_3.inner(), + )?; + + lsb + }; + + // Offset 1 + { + let offset = 1; + + // Copy j. + j.copy_advice(|| "copy j", &mut region, self.advices[5], offset)?; + // Copy z1_j. + z1_j.copy_advice(|| "copy z1_j", &mut region, self.advices[6], offset)?; + // Copy z13_j. + z13_j.copy_advice(|| "copy z13_j", &mut region, self.advices[7], offset)?; + // Copy j_prime. + j_prime.copy_advice(|| "copy j_prime", &mut region, self.advices[8], offset)?; + // Copy z13_j_prime. + z13_j_prime.copy_advice( + || "copy z13_j_prime", + &mut region, + self.advices[9], + offset, + )?; + } + + Ok(lsb) + }, + ) + } +} + +#[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, + value: ValueCanonicity, + rho: RhoCanonicity, + psi: PsiCanonicity, + y_canon: YCanonicity, + advices: [Column; 10], + sinsemilla_config: + SinsemillaConfig, +} + +#[derive(Clone, Debug)] +pub struct NoteCommitChip { + config: NoteCommitConfig, +} + +impl NoteCommitChip { + #[allow(non_snake_case)] + #[allow(clippy::many_single_char_names)] + pub(in crate::circuit) fn configure( + meta: &mut ConstraintSystem, + advices: [Column; 10], + sinsemilla_config: SinsemillaConfig< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + >, + ) -> NoteCommitConfig { + // Useful constants + let two = pallas::Base::from(2); + let two_pow_2 = pallas::Base::from(1 << 2); + let two_pow_4 = two_pow_2.square(); + let two_pow_5 = two_pow_4 * two; + let two_pow_6 = two_pow_5 * two; + let two_pow_8 = two_pow_4.square(); + let two_pow_9 = two_pow_8 * two; + let two_pow_10 = two_pow_9 * two; + let two_pow_58 = pallas::Base::from(1 << 58); + let two_pow_130 = Expression::Constant(pallas::Base::from_u128(1 << 65).square()); + let two_pow_140 = Expression::Constant(pallas::Base::from_u128(1 << 70).square()); + let two_pow_249 = pallas::Base::from_u128(1 << 124).square() * two; + let two_pow_250 = two_pow_249 * two; + let two_pow_254 = pallas::Base::from_u128(1 << 127).square(); + + let t_p = Expression::Constant(pallas::Base::from_u128(T_P)); + + // Columns used for MessagePiece and message input gates. + let col_l = advices[6]; + let col_m = advices[7]; + let col_r = advices[8]; + let col_z = advices[9]; + + let b = DecomposeB::configure(meta, col_l, col_m, col_r, two_pow_4, two_pow_5, two_pow_6); + 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 g_d = GdCanonicity::configure( + meta, + col_l, + col_m, + col_r, + col_z, + two_pow_130.clone(), + two_pow_250, + two_pow_254, + t_p.clone(), + ); + + let pk_d = PkdCanonicity::configure( + meta, + col_l, + col_m, + col_r, + col_z, + two_pow_4, + two_pow_140.clone(), + two_pow_254, + t_p.clone(), + ); + + let value = + ValueCanonicity::configure(meta, col_l, col_m, col_r, col_z, two_pow_8, two_pow_58); + + let rho = RhoCanonicity::configure( + meta, + col_l, + col_m, + col_r, + col_z, + two_pow_4, + two_pow_140, + two_pow_254, + t_p.clone(), + ); + + let psi = PsiCanonicity::configure( + meta, + col_l, + col_m, + col_r, + col_z, + two_pow_9, + two_pow_130.clone(), + two_pow_249, + two_pow_254, + t_p.clone(), + ); + + let y_canon = YCanonicity::configure( + meta, + advices, + two, + two_pow_10, + two_pow_130, + two_pow_250, + two_pow_254, + t_p, + ); + + NoteCommitConfig { + b, + d, + e, + g, + h, + g_d, + pk_d, + value, + rho, + psi, + y_canon, + advices, + sinsemilla_config, + } + } + + pub(in crate::circuit) fn construct(config: NoteCommitConfig) -> Self { + Self { config } + } +} + +pub(in crate::circuit) mod gadgets { + use halo2_proofs::circuit::{Chip, Value}; + + use super::*; + + #[allow(clippy::many_single_char_names)] + #[allow(clippy::type_complexity)] + #[allow(clippy::too_many_arguments)] + pub(in crate::circuit) fn note_commit( + mut layouter: impl Layouter, + 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> { + let lookup_config = chip.config().lookup_config(); + + // `a` = bits 0..=249 of `x(g_d)` + let a = MessagePiece::from_subpieces( + chip.clone(), + layouter.namespace(|| "a"), + [RangeConstrained::bitrange_of(g_d.x().value(), 0..250)], + )?; + + // b = b_0 || b_1 || b_2 || b_3 + // = (bits 250..=253 of x(g_d)) || (bit 254 of x(g_d)) || (ỹ bit of g_d) || (bits 0..=3 of pk★_d) + let (b, b_0, b_1, b_2, b_3) = + DecomposeB::decompose(&lookup_config, chip.clone(), &mut layouter, g_d, pk_d)?; + + // c = bits 4..=253 of pk★_d + let c = MessagePiece::from_subpieces( + chip.clone(), + layouter.namespace(|| "c"), + [RangeConstrained::bitrange_of(pk_d.x().value(), 4..254)], + )?; + + // d = d_0 || d_1 || d_2 || d_3 + // = (bit 254 of x(pk_d)) || (ỹ bit of pk_d) || (bits 0..=7 of v) || (bits 8..=57 of v) + let (d, d_0, d_1, d_2) = + DecomposeD::decompose(&lookup_config, chip.clone(), &mut layouter, pk_d, &value)?; + + // e = e_0 || e_1 = (bits 58..=63 of v) || (bits 0..=3 of rho) + let (e, e_0, e_1) = + DecomposeE::decompose(&lookup_config, chip.clone(), &mut layouter, &value, &rho)?; + + // f = bits 4..=253 inclusive of rho + let f = MessagePiece::from_subpieces( + chip.clone(), + layouter.namespace(|| "f"), + [RangeConstrained::bitrange_of(rho.value(), 4..254)], + )?; + + // g = g_0 || g_1 || g_2 + // = (bit 254 of rho) || (bits 0..=8 of psi) || (bits 9..=248 of psi) + 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)?; + + // Check decomposition of `y(g_d)`. + let b_2 = y_canonicity( + &lookup_config, + ¬e_commit_chip.config.y_canon, + layouter.namespace(|| "y(g_d) decomposition"), + g_d.y(), + b_2, + )?; + // Check decomposition of `y(pk_d)`. + let d_1 = y_canonicity( + &lookup_config, + ¬e_commit_chip.config.y_canon, + layouter.namespace(|| "y(pk_d) decomposition"), + pk_d.y(), + 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, + )? + }; + + // `CommitDomain::commit` 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 g_2 = z1_g.clone(); + let z13_g = zs[6][13].clone(); + + // Witness and constrain the bounds we need to ensure canonicity. + let (a_prime, z13_a_prime) = canon_bitshift_130( + &lookup_config, + layouter.namespace(|| "x(g_d) canonicity"), + a.inner().cell_value(), + )?; + + let (b3_c_prime, z14_b3_c_prime) = pkd_x_canonicity( + &lookup_config, + layouter.namespace(|| "x(pk_d) canonicity"), + b_3.clone(), + c.inner().cell_value(), + )?; + + let (e1_f_prime, z14_e1_f_prime) = rho_canonicity( + &lookup_config, + layouter.namespace(|| "rho canonicity"), + e_1.clone(), + f.inner().cell_value(), + )?; + + let (g1_g2_prime, z13_g1_g2_prime) = psi_canonicity( + &lookup_config, + layouter.namespace(|| "psi canonicity"), + g_1.clone(), + g_2, + )?; + + // Finally, assign values to all of the NoteCommit regions. + let cfg = note_commit_chip.config; + + let b_1 = cfg + .b + .assign(&mut layouter, b, b_0.clone(), b_1, b_2, b_3.clone())?; + + let d_0 = cfg + .d + .assign(&mut layouter, d, d_0, d_1, d_2.clone(), z1_d.clone())?; + + cfg.e.assign(&mut layouter, e, e_0.clone(), e_1.clone())?; + + let g_0 = cfg + .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)?; + + cfg.g_d + .assign(&mut layouter, g_d, a, b_0, b_1, a_prime, z13_a, z13_a_prime)?; + + cfg.pk_d.assign( + &mut layouter, + pk_d, + b_3, + c, + d_0, + b3_c_prime, + z13_c, + z14_b3_c_prime, + )?; + + cfg.value.assign(&mut layouter, value, d_2, z1_d, e_0)?; + + cfg.rho.assign( + &mut layouter, + rho, + e_1, + f, + g_0, + e1_f_prime, + z13_f, + z14_e1_f_prime, + )?; + + cfg.psi.assign( + &mut layouter, + psi, + g_1, + z1_g, + h_0, + h_1, + g1_g2_prime, + z13_g, + z13_g1_g2_prime, + )?; + + Ok(cm) + } + + /// A canonicity check helper used in checking x(g_d), y(g_d), and y(pk_d). + /// + /// 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, + mut layouter: impl Layouter, + a: AssignedCell, + ) -> Result { + // element = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)` + // - b_1 = 1 => b_0 = 0 + // - b_1 = 1 => a < t_P + // - 0 ≤ a < 2^130 (z_13 of SinsemillaHash(a)) + // - 0 ≤ a + 2^130 - t_P < 2^130 (thirteen 10-bit lookups) + + // Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output + // the running sum at the end of it. If a_prime < 2^130, the running sum + // will be 0. + let a_prime = { + let two_pow_130 = Value::known(pallas::Base::from_u128(1u128 << 65).square()); + let t_p = Value::known(pallas::Base::from_u128(T_P)); + a.value() + two_pow_130 - t_p + }; + let zs = lookup_config.witness_check( + layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"), + a_prime, + 13, + false, + )?; + let a_prime = zs[0].clone(); + assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13] + + Ok((a_prime, zs[13].clone())) + } + + /// Check canonicity of `x(pk_d)` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-pk_d?partial). + fn pkd_x_canonicity( + lookup_config: &LookupRangeCheckConfig, + mut layouter: impl Layouter, + b_3: RangeConstrained>, + c: AssignedCell, + ) -> Result { + // `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 + // - b_3 is part of the Sinsemilla message piece + // b = b_0 (4 bits) || b_1 (1 bit) || b_2 (1 bit) || b_3 (4 bits) + // - b_3 is individually constrained to be 4 bits. + // - 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) + + // 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. + // If b3_c_prime < 2^140, the running sum will be 0. + let b3_c_prime = { + let two_pow_4 = Value::known(pallas::Base::from(1u64 << 4)); + let two_pow_140 = Value::known(pallas::Base::from_u128(1u128 << 70).square()); + let t_p = Value::known(pallas::Base::from_u128(T_P)); + b_3.inner().value() + (two_pow_4 * c.value()) + two_pow_140 - t_p + }; + + let zs = lookup_config.witness_check( + layouter.namespace(|| "Decompose low 140 bits of (b_3 + 2^4 c + 2^140 - t_P)"), + b3_c_prime, + 14, + false, + )?; + let b3_c_prime = zs[0].clone(); + assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] + + Ok((b3_c_prime, zs[14].clone())) + } + + /// Check canonicity of `rho` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-rho?partial). + fn rho_canonicity( + lookup_config: &LookupRangeCheckConfig, + mut layouter: impl Layouter, + e_1: RangeConstrained>, + f: AssignedCell, + ) -> Result { + // `rho` = `e_1 (4 bits) || f (250 bits) || g_0 (1 bit)` + // - g_0 = 1 => e_1 + 2^4 f < t_P + // - 0 ≤ e_1 + 2^4 f < 2^134 + // - e_1 is part of the Sinsemilla message piece + // e = e_0 (56 bits) || e_1 (4 bits) + // - e_1 is individually constrained to be 4 bits. + // - z_13 of SinsemillaHash(f) == 0 constrains bits 4..=253 of rho + // to 130 bits. z13_f == 0 is directly checked in the gate. + // - 0 ≤ e_1 + 2^4 f + 2^140 - t_P < 2^140 (14 ten-bit lookups) + + let e1_f_prime = { + let two_pow_4 = Value::known(pallas::Base::from(1u64 << 4)); + let two_pow_140 = Value::known(pallas::Base::from_u128(1u128 << 70).square()); + let t_p = Value::known(pallas::Base::from_u128(T_P)); + e_1.inner().value() + (two_pow_4 * f.value()) + two_pow_140 - t_p + }; + + // Decompose the low 140 bits of e1_f_prime = e_1 + 2^4 f + 2^140 - t_P, + // and output the running sum at the end of it. + // If e1_f_prime < 2^140, the running sum will be 0. + let zs = lookup_config.witness_check( + layouter.namespace(|| "Decompose low 140 bits of (e_1 + 2^4 f + 2^140 - t_P)"), + e1_f_prime, + 14, + false, + )?; + let e1_f_prime = zs[0].clone(); + assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14] + + Ok((e1_f_prime, zs[14].clone())) + } + + /// Check canonicity of `psi` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-psi?partial). + fn psi_canonicity( + lookup_config: &LookupRangeCheckConfig, + mut layouter: impl Layouter, + g_1: RangeConstrained>, + g_2: AssignedCell, + ) -> Result { + // `psi` = `g_1 (9 bits) || g_2 (240 bits) || h_0 (5 bits) || h_1 (1 bit)` + // - h_1 = 1 => (h_0 = 0) ∧ (g_1 + 2^9 g_2 < t_P) + // - 0 ≤ g_1 + 2^9 g_2 < 2^130 + // - g_1 is individually constrained to be 9 bits + // - z_13 of SinsemillaHash(g) == 0 constrains bits 0..=248 of psi + // to 130 bits. z13_g == 0 is directly checked in the gate. + // - 0 ≤ g_1 + (2^9)g_2 + 2^130 - t_P < 2^130 (13 ten-bit lookups) + + // Decompose the low 130 bits of g1_g2_prime = g_1 + (2^9)g_2 + 2^130 - t_P, + // and output the running sum at the end of it. + // If g1_g2_prime < 2^130, the running sum will be 0. + let g1_g2_prime = { + let two_pow_9 = Value::known(pallas::Base::from(1u64 << 9)); + let two_pow_130 = Value::known(pallas::Base::from_u128(1u128 << 65).square()); + let t_p = Value::known(pallas::Base::from_u128(T_P)); + g_1.inner().value() + (two_pow_9 * g_2.value()) + two_pow_130 - t_p + }; + + let zs = lookup_config.witness_check( + layouter.namespace(|| "Decompose low 130 bits of (g_1 + (2^9)g_2 + 2^130 - t_P)"), + g1_g2_prime, + 13, + false, + )?; + let g1_g2_prime = zs[0].clone(); + assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13] + + Ok((g1_g2_prime, zs[13].clone())) + } + + /// Check canonicity of y-coordinate given its LSB as a value. + /// Also, witness the LSB and return the witnessed cell. + /// + /// 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, + y_canon: &YCanonicity, + mut layouter: impl Layouter, + y: AssignedCell, + lsb: RangeConstrained>, + ) -> Result>, Error> + { + // Decompose the field element + // y = LSB || k_0 || k_1 || k_2 || k_3 + // = (bit 0) || (bits 1..=9) || (bits 10..=249) || (bits 250..=253) || (bit 254) + + // Range-constrain k_0 to be 9 bits. + let k_0 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "k_0"), + y.value(), + 1..10, + )?; + + // k_1 will be constrained by the decomposition of j. + let k_1 = RangeConstrained::bitrange_of(y.value(), 10..250); + + // Range-constrain k_2 to be 4 bits. + let k_2 = RangeConstrained::witness_short( + lookup_config, + layouter.namespace(|| "k_2"), + y.value(), + 250..254, + )?; + + // k_3 will be boolean-constrained in the gate. + let k_3 = RangeConstrained::bitrange_of(y.value(), 254..255); + + // Decompose j = LSB + (2)k_0 + (2^10)k_1 using 25 ten-bit lookups. + let (j, z1_j, z13_j) = { + let j = { + let two = Value::known(pallas::Base::from(2)); + let two_pow_10 = Value::known(pallas::Base::from(1 << 10)); + lsb.inner().value() + two * k_0.inner().value() + two_pow_10 * k_1.inner().value() + }; + let zs = lookup_config.witness_check( + layouter.namespace(|| "Decompose j = LSB + (2)k_0 + (2^10)k_1"), + j, + 25, + true, + )?; + (zs[0].clone(), zs[1].clone(), zs[13].clone()) + }; + + // Decompose j_prime = j + 2^130 - t_P using 13 ten-bit lookups. + // We can reuse the canon_bitshift_130 logic here. + let (j_prime, z13_j_prime) = canon_bitshift_130( + lookup_config, + layouter.namespace(|| "j_prime = j + 2^130 - t_P"), + j.clone(), + )?; + + y_canon.assign( + &mut layouter, + y, + lsb, + k_0, + k_2, + k_3, + j, + z1_j, + z13_j, + j_prime, + z13_j_prime, + ) + } +} + +#[cfg(test)] +mod tests { + use core::iter; + + use super::NoteCommitConfig; + use crate::{ + circuit::circuit_vanilla::{ + gadget::assign_free_advice, + note_commit::{gadgets, NoteCommitChip}, + }, + constants::{ + fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases, + OrchardHashDomains, L_ORCHARD_BASE, L_VALUE, T_Q, + }, + value::NoteValue, + }; + use halo2_gadgets::{ + ecc::{ + chip::{EccChip, EccConfig}, + NonIdentityPoint, ScalarFixed, + }, + sinsemilla::chip::SinsemillaChip, + sinsemilla::primitives::CommitDomain, + utilities::lookup_range_check::LookupRangeCheckConfig, + }; + + use ff::{Field, PrimeField, PrimeFieldBits}; + use group::Curve; + use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + dev::MockProver, + plonk::{Circuit, ConstraintSystem, Error}, + }; + use pasta_curves::{arithmetic::CurveAffine, pallas}; + + use rand::{rngs::OsRng, RngCore}; + + #[test] + fn note_commit() { + #[derive(Default)] + struct MyCircuit { + gd_x: Value, + gd_y_lsb: Value, + pkd_x: Value, + pkd_y_lsb: Value, + rho: Value, + psi: Value, + } + + impl Circuit for MyCircuit { + type Config = (NoteCommitConfig, EccConfig); + 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 lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + None, + ); + 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, None); + let sinsemilla_config = SinsemillaChip::< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + >::configure( + meta, + advices[..5].try_into().unwrap(), + advices[2], + lagrange_coeffs[0], + lookup, + range_check, + ); + let note_commit_config = + NoteCommitChip::configure(meta, advices, sinsemilla_config); + + let ecc_config = EccChip::::configure( + meta, + advices, + lagrange_coeffs, + range_check, + ); + + (note_commit_config, ecc_config) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let (note_commit_config, ecc_config) = config; + + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::< + OrchardHashDomains, + OrchardCommitDomains, + OrchardFixedBases, + >::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); + + // Construct a NoteCommit chip + let note_commit_chip = NoteCommitChip::construct(note_commit_config.clone()); + + // Witness g_d + let g_d = { + let g_d = self.gd_x.zip(self.gd_y_lsb).map(|(x, y_lsb)| { + // Calculate y = (x^3 + 5).sqrt() + let mut y = (x.square() * x + pallas::Affine::b()).sqrt().unwrap(); + if bool::from(y.is_odd() ^ y_lsb.is_odd()) { + y = -y; + } + pallas::Affine::from_xy(x, y).unwrap() + }); + + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness g_d"), + g_d, + )? + }; + + // Witness pk_d + let pk_d = { + let pk_d = self.pkd_x.zip(self.pkd_y_lsb).map(|(x, y_lsb)| { + // Calculate y = (x^3 + 5).sqrt() + let mut y = (x.square() * x + pallas::Affine::b()).sqrt().unwrap(); + if bool::from(y.is_odd() ^ y_lsb.is_odd()) { + y = -y; + } + pallas::Affine::from_xy(x, y).unwrap() + }); + + NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness pk_d"), + 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 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, + )?; + let expected_cm = { + let domain = CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION); + // Hash g★_d || pk★_d || i2lebsp_{64}(v) || rho || psi + let lsb = |y_lsb: pallas::Base| y_lsb == pallas::Base::one(); + let point = self + .gd_x + .zip(self.gd_y_lsb) + .zip(self.pkd_x.zip(self.pkd_y_lsb)) + .zip(self.rho.zip(self.psi)) + .map(|(((gd_x, gd_y_lsb), (pkd_x, pkd_y_lsb)), (rho, psi))| { + domain + .commit( + iter::empty() + .chain( + gd_x.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE), + ) + .chain(Some(lsb(gd_y_lsb))) + .chain( + pkd_x + .to_le_bits() + .iter() + .by_vals() + .take(L_ORCHARD_BASE), + ) + .chain(Some(lsb(pkd_y_lsb))) + .chain(value.to_le_bits().iter().by_vals().take(L_VALUE)) + .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, + ) + .unwrap() + .to_affine() + }); + NonIdentityPoint::new(ecc_chip, layouter.namespace(|| "witness cm"), point)? + }; + cm.constrain_equal(layouter.namespace(|| "cm == expected cm"), &expected_cm) + } + } + + let two_pow_254 = pallas::Base::from_u128(1 << 127).square(); + // Test different values of `ak`, `nk` + let circuits = [ + // `gd_x` = -1, `pkd_x` = -1 (these have to be x-coordinates of curve points) + // `rho` = 0, `psi` = 0 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::one()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::one()), + rho: Value::known(pallas::Base::zero()), + psi: Value::known(pallas::Base::zero()), + }, + // `rho` = T_Q - 1, `psi` = T_Q - 1 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::zero()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::zero()), + rho: Value::known(pallas::Base::from_u128(T_Q - 1)), + psi: Value::known(pallas::Base::from_u128(T_Q - 1)), + }, + // `rho` = T_Q, `psi` = T_Q + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::one()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::zero()), + rho: Value::known(pallas::Base::from_u128(T_Q)), + psi: Value::known(pallas::Base::from_u128(T_Q)), + }, + // `rho` = 2^127 - 1, `psi` = 2^127 - 1 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::zero()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::one()), + rho: Value::known(pallas::Base::from_u128((1 << 127) - 1)), + psi: Value::known(pallas::Base::from_u128((1 << 127) - 1)), + }, + // `rho` = 2^127, `psi` = 2^127 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::zero()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::zero()), + rho: Value::known(pallas::Base::from_u128(1 << 127)), + psi: Value::known(pallas::Base::from_u128(1 << 127)), + }, + // `rho` = 2^254 - 1, `psi` = 2^254 - 1 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::one()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::one()), + rho: Value::known(two_pow_254 - pallas::Base::one()), + psi: Value::known(two_pow_254 - pallas::Base::one()), + }, + // `rho` = 2^254, `psi` = 2^254 + MyCircuit { + gd_x: Value::known(-pallas::Base::one()), + gd_y_lsb: Value::known(pallas::Base::one()), + pkd_x: Value::known(-pallas::Base::one()), + pkd_y_lsb: Value::known(pallas::Base::zero()), + rho: Value::known(two_pow_254), + psi: Value::known(two_pow_254), + }, + ]; + + for circuit in circuits.iter() { + let prover = MockProver::::run(11, circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())); + } + } +} diff --git a/src/circuit/circuit_zsa.rs b/src/circuit/circuit_zsa.rs new file mode 100644 index 000000000..b4d49f1cb --- /dev/null +++ b/src/circuit/circuit_zsa.rs @@ -0,0 +1,1421 @@ +//! The Orchard Action circuit implementation. + +// FIXME: rename to zsa.rs (as it's alredy in circuit folder)? + +use ff::Field; + +use group::Curve; + +use pasta_curves::{arithmetic::CurveAffine, pallas}; + +use halo2_gadgets::{ + ecc::{ + chip::{EccChip, EccConfig}, + FixedPoint, NonIdentityPoint, Point, ScalarFixed, ScalarVar, + }, + poseidon::{primitives as poseidon, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig}, + sinsemilla::{ + chip::{SinsemillaChip, SinsemillaConfig}, + merkle::{ + chip::{MerkleChip, MerkleConfig}, + MerklePath, + }, + }, + utilities::{ + bool_check, + cond_swap::{CondSwapChip, CondSwapConfig}, + lookup_range_check::LookupRangeCheckConfig, + }, +}; + +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::{self, Advice, Column, Constraints, Expression, Instance as InstanceColumn, Selector}, + poly::Rotation, +}; + +use crate::{ + constants::OrchardFixedBasesFull, + constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, + note::AssetBase, + orchard_flavors::OrchardZSA, +}; + +use super::{ + commit_ivk::{ + self, {CommitIvkChip, CommitIvkConfig}, + }, + gadget::{ + add_chip::{self, AddChip, AddConfig}, + AddInstruction, + }, + CircuitBase, OrchardCircuit, ANCHOR, CMX, CV_NET_X, CV_NET_Y, ENABLE_OUTPUT, ENABLE_SPEND, + ENABLE_ZSA, NF_OLD, RK_X, RK_Y, +}; + +use self::{ + gadget::{assign_free_advice, assign_is_native_asset, assign_split_flag}, + note_commit::{NoteCommitChip, NoteCommitConfig}, +}; + +pub mod gadget; +mod note_commit; +mod value_commit_orchard; + +/// Configuration needed to use the Orchard Action circuit. +#[derive(Clone, Debug)] +pub struct Config { + primary: Column, + q_orchard: Selector, + advices: [Column; 10], + add_config: AddConfig, + ecc_config: EccConfig, + poseidon_config: PoseidonConfig, + merkle_config_1: MerkleConfig, + merkle_config_2: MerkleConfig, + sinsemilla_config_1: + SinsemillaConfig, + sinsemilla_config_2: + SinsemillaConfig, + commit_ivk_config: CommitIvkConfig, + old_note_commit_config: NoteCommitConfig, + new_note_commit_config: NoteCommitConfig, + cond_swap_config: CondSwapConfig, +} + +impl OrchardCircuit for OrchardZSA { + type Config = Config; + + fn configure(meta: &mut plonk::ConstraintSystem) -> Self::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(), + ]; + + // Constrain split_flag to be boolean + // Constrain v_old * (1 - split_flag) - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). + // Constrain (v_old = 0 and is_native_asset = 1) or (calculated root = anchor) (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + // Constrain v_old = 0 or enable_spends = 1 (https://p.z.cash/ZKS:action-enable-spend). + // Constrain v_new = 0 or enable_outputs = 1 (https://p.z.cash/ZKS:action-enable-output). + // Constrain is_native_asset to be boolean + // Constraint if is_native_asset = 1 then asset = native_asset else asset != native_asset + // Constraint if split_flag = 0 then psi_old = psi_nf + // Constraint if split_flag = 1, then is_native_asset = 0 + // Constraint if enable_zsa = 0, then is_native_asset = 1 + 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_spends = meta.query_advice(advices[6], Rotation::cur()); + let enable_outputs = meta.query_advice(advices[7], Rotation::cur()); + + let split_flag = meta.query_advice(advices[8], Rotation::cur()); + + let is_native_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 native_asset = AssetBase::native() + .cv_base() + .to_affine() + .coordinates() + .unwrap(); + + let diff_asset_x = asset_x - Expression::Constant(*native_asset.x()); + let diff_asset_y = asset_y - Expression::Constant(*native_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()); + + 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_native_asset is boolean (just below), and + // * v_old is a 64 bit unsigned integer (in the note commitment evaluation). + // So, 1 - is_native_asset + v_old = 0 only when (is_native_asset = 1 and v_old = 0), no overflow can occur. + ( + "(v_old = 0 and is_native_asset = 1) or (root = anchor)", + (v_old.clone() + one.clone() - is_native_asset.clone()) * (root - anchor), + ), + ( + "v_old = 0 or enable_spends = 1", + v_old * (one.clone() - enable_spends), + ), + ( + "v_new = 0 or enable_outputs = 1", + v_new * (one.clone() - enable_outputs), + ), + ( + "bool_check is_native_asset", + bool_check(is_native_asset.clone()), + ), + ( + "(is_native_asset = 1) => (asset_x = native_asset_x)", + is_native_asset.clone() * diff_asset_x.clone(), + ), + ( + "(is_native_asset = 1) => (asset_y = native_asset_y)", + is_native_asset.clone() * diff_asset_y.clone(), + ), + // To prove that `asset` is not equal to `native_asset`, we will prove that at + // least one of `x(asset) - x(native_asset)` or `y(asset) - y(native_asset)` is + // not equal to zero. + // To prove that `x(asset) - x(native_asset)` (resp `y(asset) - y(native_asset)`) + // is not equal to zero, we will prove that it is invertible. + ( + "(is_native_asset = 0) => (asset != native_asset)", + (one.clone() - is_native_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_native_asset = 0)", + split_flag * is_native_asset.clone(), + ), + ( + "(enable_zsa = 0) => (is_native_asset = 1)", + (one.clone() - enable_zsa) * (one - is_native_asset), + ), + ], + ) + }); + + // 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 table_range_check_tag = meta.lookup_table_column(); + let lookup = ( + table_idx, + meta.lookup_table_column(), + meta.lookup_table_column(), + Some(table_range_check_tag), + ); + + // 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. + let range_check = LookupRangeCheckConfig::configure( + meta, + advices[9], + table_idx, + Some(table_range_check_tag), + ); + + // 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, + ); + 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, + ); + let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone()); + + (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()); + + let cond_swap_config = CondSwapChip::configure(meta, advices[0..5].try_into().unwrap()); + + 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, + cond_swap_config, + } + } + + #[allow(non_snake_case)] + fn synthesize( + circuit: &CircuitBase, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), plonk::Error> { + // Load the Sinsemilla generator lookup table used by the whole circuit. + SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?; + + // Construct the ECC chip. + let ecc_chip = config.ecc_chip(); + + // 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], + circuit.psi_nf, + )?; + + // Witness psi_old + let psi_old = assign_free_advice( + layouter.namespace(|| "witness psi_old"), + config.advices[0], + circuit.psi_old, + )?; + + // Witness rho_old + let rho_old = assign_free_advice( + layouter.namespace(|| "witness rho_old"), + config.advices[0], + circuit.rho_old.map(|rho| rho.0), + )?; + + // Witness cm_old + let cm_old = Point::new( + ecc_chip.clone(), + layouter.namespace(|| "cm_old"), + circuit.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"), + circuit.g_d_old.as_ref().map(|gd| gd.to_affine()), + )?; + + // Witness ak_P. + let ak_P: Value = circuit.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], + circuit.nk.map(|nk| nk.inner()), + )?; + + // Witness v_old. + let v_old = assign_free_advice( + layouter.namespace(|| "witness v_old"), + config.advices[0], + circuit.v_old, + )?; + + // Witness v_new. + let v_new = assign_free_advice( + layouter.namespace(|| "witness v_new"), + config.advices[0], + circuit.v_new, + )?; + + // Witness asset + let asset = NonIdentityPoint::new( + ecc_chip.clone(), + layouter.namespace(|| "witness asset"), + circuit.asset.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], + circuit.split_flag, + )?; + + // Witness is_native_asset which is equal to + // 1 if asset is equal to native asset, and + // 0 if asset is not equal to native asset. + let is_native_asset = assign_is_native_asset( + layouter.namespace(|| "witness is_native_asset"), + config.advices[0], + circuit.asset, + )?; + + // Merkle path validity check (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + let root = { + let path = circuit + .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, + circuit.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 = { + // v_net is equal to + // (-v_new) if split_flag = true + // v_old - v_new if split_flag = false + let v_net = circuit.split_flag.and_then(|split_flag| { + if split_flag { + Value::known(crate::value::NoteValue::zero()) - circuit.v_new + } else { + circuit.v_old - circuit.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"), + circuit.rcv.as_ref().map(|rcv| rcv.inner()), + )?; + + let cv_net = gadget::value_commit_orchard( + layouter.namespace(|| "cv_net = ValueCommit^Orchard_rcv(v_net_magnitude_sign)"), + config.sinsemilla_chip_1(), + ecc_chip.clone(), + v_net_magnitude_sign.clone(), + rcv, + 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 (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_nf, cm_old)"), + config.poseidon_chip(), + config.add_chip(), + ecc_chip.clone(), + config.cond_swap_chip(), + rho_old.clone(), + &psi_nf, + &cm_old, + nk.clone(), + 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 (https://p.z.cash/ZKS:action-spend-authority) + { + let alpha = ScalarFixed::new( + ecc_chip.clone(), + layouter.namespace(|| "alpha"), + circuit.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"), + circuit.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"), + circuit + .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"), + circuit.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(), + config.note_commit_chip_old(), + config.cond_swap_chip(), + g_d_old.inner(), + pk_d_old.inner(), + v_old.clone(), + rho_old, + psi_old.clone(), + asset.inner(), + rcm_old, + is_native_asset.clone(), + )?; + + // Constrain derived cm_old to equal witnessed cm_old + derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; + } + + // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). + { + // Witness g_d_new + let g_d_new = { + let g_d_new = circuit.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 = circuit + .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 = 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], + circuit.psi_new, + )?; + + let rcm_new = ScalarFixed::new( + ecc_chip, + layouter.namespace(|| "rcm_new"), + circuit.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(), + config.note_commit_chip_new(), + config.cond_swap_chip(), + g_d_new.inner(), + pk_d_new.inner(), + v_new.clone(), + rho_new, + psi_new, + asset.inner(), + rcm_new, + is_native_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 spends", + config.primary, + ENABLE_SPEND, + config.advices[6], + 0, + )?; + + region.assign_advice_from_instance( + || "enable outputs", + config.primary, + ENABLE_OUTPUT, + config.advices[7], + 0, + )?; + + split_flag.copy_advice(|| "split_flag", &mut region, config.advices[8], 0)?; + + is_native_asset.copy_advice( + || "is_native_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_native_asset = 0, then asset != native_asset. + region.assign_advice( + || "diff_asset_x_inv", + config.advices[2], + 1, + || { + circuit.asset.map(|asset| { + let asset_x = *asset.cv_base().to_affine().coordinates().unwrap().x(); + let native_asset_x = *AssetBase::native() + .cv_base() + .to_affine() + .coordinates() + .unwrap() + .x(); + + let diff_asset_x = asset_x - native_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, + || { + circuit.asset.map(|asset| { + let asset_y = *asset.cv_base().to_affine().coordinates().unwrap().y(); + let native_asset_y = *AssetBase::native() + .cv_base() + .to_affine() + .coordinates() + .unwrap() + .y(); + + let diff_asset_y = asset_y - native_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, + )?; + + config.q_orchard.enable(&mut region, 0) + }, + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use core::iter; + + use ff::Field; + use group::{Curve, Group, GroupEncoding}; + use halo2_proofs::{circuit::Value, dev::MockProver}; + use pasta_curves::pallas; + use rand::{rngs::OsRng, RngCore}; + + use crate::{ + builder::SpendInfo, + bundle::Flags, + circuit::{CircuitBase, Instance, Proof, ProvingKey, VerifyingKey, K}, + keys::{FullViewingKey, Scope, SpendValidatingKey, SpendingKey}, + note::{commitment::NoteCommitTrapdoor, AssetBase, Note, NoteCommitment, Nullifier}, + orchard_flavors::OrchardZSA, + primitives::redpallas::VerificationKey, + tree::MerklePath, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + }; + + type OrchardCircuitZSA = CircuitBase; + + fn generate_dummy_circuit_instance(mut rng: R) -> (OrchardCircuitZSA, Instance) { + let (_, fvk, spent_note) = Note::dummy(&mut rng, None, AssetBase::native()); + + 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 ak: SpendValidatingKey = fvk.into(); + let alpha = pallas::Scalar::random(&mut rng); + let rk = ak.randomize(&alpha); + + let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetBase::native()); + 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, AssetBase::native()); + + let path = MerklePath::dummy(&mut rng); + let anchor = path.root(spent_note.commitment().into()); + + let psi_old = spent_note.rseed().psi(&spent_note.rho()); + + ( + OrchardCircuitZSA { + 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(psi_old), + rcm_old: Value::known(spent_note.rseed().rcm(&spent_note.rho())), + cm_old: Value::known(spent_note.commitment()), + // For non split note, psi_nf is equal to psi_old + psi_nf: Value::known(psi_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.rseed().psi(&output_note.rho())), + rcm_new: Value::known(output_note.rseed().rcm(&output_note.rho())), + rcv: Value::known(rcv), + asset: Value::known(spent_note.asset()), + split_flag: Value::known(false), + phantom: std::marker::PhantomData, + }, + Instance { + anchor, + cv_net, + nf_old, + rk, + cmx, + enable_spend: true, + enable_output: true, + enable_zsa: false, + }, + ) + } + + // TODO: recast as a proptest + #[test] + fn round_trip() { + let mut rng = OsRng; + + let (circuits, instances): (Vec<_>, Vec<_>) = iter::once(()) + .map(|()| generate_dummy_circuit_instance(&mut rng)) + .unzip(); + + let vk = VerifyingKey::build::(); + + // Test that the pinned verification key (representing the circuit) + // is as expected. + { + // panic!("{:#?}", vk.vk.pinned()); + assert_eq!( + format!("{:#?}\n", vk.vk.pinned()), + include_str!("circuit_description_zsa").replace("\r\n", "\n") + ); + } + + // Test that the proof size is as expected. + let expected_proof_size = { + let circuit_cost = + halo2_proofs::dev::CircuitCost::::measure( + K, + &circuits[0], + ); + assert_eq!(usize::from(circuit_cost.proof_size(1)), 5120); + assert_eq!(usize::from(circuit_cost.proof_size(2)), 7392); + 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::(); + 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 serialized_proof_test_case() { + use std::fs; + use std::io::{Read, Write}; + + let vk = VerifyingKey::build::(); + + fn write_test_case( + mut w: W, + instance: &Instance, + proof: &Proof, + ) -> 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.clone()))?; + w.write_all(&instance.cmx.to_bytes())?; + w.write_all(&[ + u8::from(instance.enable_spend), + u8::from(instance.enable_output), + u8::from(instance.enable_zsa), + ])?; + + w.write_all(proof.as_ref())?; + Ok(()) + } + + fn read_test_case(mut r: R) -> 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 enable_zsa = read_bool(&mut r); + let instance = Instance::from_parts( + anchor, + cv_net, + nf_old, + rk, + cmx, + Flags::from_parts(enable_spend, enable_output, enable_zsa), + ); + + let mut proof_bytes = vec![]; + r.read_to_end(&mut proof_bytes)?; + let proof = Proof::new(proof_bytes); + + Ok((instance, proof)) + } + + 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, instance) = generate_dummy_circuit_instance(OsRng); + let instances = &[instance.clone()]; + + let pk = ProvingKey::build::(); + let proof = Proof::create(&pk, &[circuit], instances, &mut rng).unwrap(); + assert!(proof.verify(&vk, instances).is_ok()); + + let file = std::fs::File::create("src/circuit/circuit_proof_test_case_zsa.bin")?; + write_test_case(file, &instance, &proof) + }; + create_proof().expect("should be able to write new proof"); + } + + // Parse the hardcoded proof test case. + let (instance, proof) = { + let test_case_bytes = fs::read("src/circuit/circuit_proof_test_case_zsa.bin").unwrap(); + read_test_case(&test_case_bytes[..]).expect("proof must be valid") + }; + assert_eq!(proof.0.len(), 5120); + + 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 = OrchardCircuitZSA { + 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(), + psi_nf: 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(), + asset: Value::unknown(), + split_flag: Value::unknown(), + phantom: std::marker::PhantomData, + }; + halo2_proofs::dev::CircuitLayout::default() + .show_labels(false) + .view_height(0..(1 << 11)) + .render(K, &circuit, &root) + .unwrap(); + } + + fn check_proof_of_orchard_circuit( + circuit: &OrchardCircuitZSA, + instance: &Instance, + should_pass: bool, + ) { + let proof_verify = MockProver::run( + K, + circuit, + instance + .to_halo2_instance() + .iter() + .map(|p| p.to_vec()) + .collect(), + ) + .unwrap() + .verify(); + if should_pass { + assert!(proof_verify.is_ok()); + } else { + assert!(proof_verify.is_err()); + } + } + + fn generate_circuit_instance( + is_native_asset: bool, + split_flag: bool, + mut rng: R, + ) -> (OrchardCircuitZSA, Instance) { + // Create asset + let asset_base = if is_native_asset { + AssetBase::native() + } else { + AssetBase::random() + }; + + // Create spent_note + let (spent_note_fvk, spent_note) = { + let sk = SpendingKey::random(&mut rng); + let fvk: FullViewingKey = (&sk).into(); + let sender_address = fvk.address_at(0u32, Scope::External); + let rho_old = Nullifier::dummy(&mut rng); + let note = Note::new( + sender_address, + NoteValue::from_raw(40), + asset_base, + rho_old, + &mut rng, + ); + let spent_note = if split_flag { + note.create_split_note(&mut rng) + } else { + note + }; + (fvk, spent_note) + }; + + let output_value = NoteValue::from_raw(10); + + let (scope, v_net) = if split_flag { + ( + Scope::External, + // Split notes do not contribute to v_net. + // Therefore, if split_flag is true, v_net = - output_value + NoteValue::zero() - output_value, + ) + } else { + ( + spent_note_fvk + .scope_for_address(&spent_note.recipient()) + .unwrap(), + spent_note.value() - output_value, + ) + }; + + let nf_old = spent_note.nullifier(&spent_note_fvk); + let ak: SpendValidatingKey = spent_note_fvk.clone().into(); + let alpha = pallas::Scalar::random(&mut rng); + let rk = ak.randomize(&alpha); + + let output_note = { + let sk = SpendingKey::random(&mut rng); + let fvk: FullViewingKey = (&sk).into(); + let sender_address = fvk.address_at(0u32, Scope::External); + + Note::new(sender_address, output_value, asset_base, nf_old, &mut rng) + }; + + let cmx = output_note.commitment().into(); + + let rcv = ValueCommitTrapdoor::random(&mut rng); + let cv_net = ValueCommitment::derive(v_net, rcv, asset_base); + + let path = MerklePath::dummy(&mut rng); + let anchor = path.root(spent_note.commitment().into()); + + let spend_info = SpendInfo { + dummy_sk: None, + fvk: spent_note_fvk, + scope, + note: spent_note, + merkle_path: path, + split_flag, + }; + + ( + OrchardCircuitZSA::from_action_context_unchecked(spend_info, output_note, alpha, rcv), + Instance { + anchor, + cv_net, + nf_old, + rk, + cmx, + enable_spend: true, + enable_output: true, + enable_zsa: true, + }, + ) + } + + fn random_note_commitment(mut rng: impl RngCore) -> NoteCommitment { + NoteCommitment::derive( + pallas::Point::random(&mut rng).to_affine().to_bytes(), + pallas::Point::random(&mut rng).to_affine().to_bytes(), + NoteValue::from_raw(rng.next_u64()), + AssetBase::random(), + pallas::Base::random(&mut rng), + pallas::Base::random(&mut rng), + NoteCommitTrapdoor(pallas::Scalar::random(&mut rng)), + ) + .unwrap() + } + + #[test] + fn orchard_circuit_negative_test() { + let mut rng = OsRng; + + for is_native_asset in [true, false] { + for split_flag in [true, false] { + let (circuit, instance) = + generate_circuit_instance(is_native_asset, split_flag, &mut rng); + + let should_pass = !(matches!((is_native_asset, split_flag), (true, true))); + + check_proof_of_orchard_circuit(&circuit, &instance, should_pass); + + // Set cv_net to be zero + // The proof should fail + let instance_wrong_cv_net = Instance { + anchor: instance.anchor, + cv_net: ValueCommitment::from_bytes(&[0u8; 32]).unwrap(), + nf_old: instance.nf_old, + rk: instance.rk.clone(), + cmx: instance.cmx, + enable_spend: instance.enable_spend, + enable_output: instance.enable_output, + enable_zsa: instance.enable_zsa, + }; + check_proof_of_orchard_circuit(&circuit, &instance_wrong_cv_net, false); + + // Set rk_pub to be a dummy VerificationKey + // The proof should fail + let instance_wrong_rk = Instance { + anchor: instance.anchor, + cv_net: instance.cv_net.clone(), + nf_old: instance.nf_old, + rk: VerificationKey::dummy(), + cmx: instance.cmx, + enable_spend: instance.enable_spend, + enable_output: instance.enable_output, + enable_zsa: instance.enable_zsa, + }; + check_proof_of_orchard_circuit(&circuit, &instance_wrong_rk, false); + + // Set cm_old to be a random NoteCommitment + // The proof should fail + let circuit_wrong_cm_old = OrchardCircuitZSA { + path: circuit.path, + pos: circuit.pos, + g_d_old: circuit.g_d_old, + pk_d_old: circuit.pk_d_old, + v_old: circuit.v_old, + rho_old: circuit.rho_old, + psi_old: circuit.psi_old, + rcm_old: circuit.rcm_old.clone(), + cm_old: Value::known(random_note_commitment(&mut rng)), + psi_nf: circuit.psi_nf, + alpha: circuit.alpha, + ak: circuit.ak.clone(), + nk: circuit.nk, + rivk: circuit.rivk, + g_d_new: circuit.g_d_new, + pk_d_new: circuit.pk_d_new, + v_new: circuit.v_new, + psi_new: circuit.psi_new, + rcm_new: circuit.rcm_new.clone(), + rcv: circuit.rcv, + asset: circuit.asset, + split_flag: circuit.split_flag, + phantom: std::marker::PhantomData, + }; + check_proof_of_orchard_circuit(&circuit_wrong_cm_old, &instance, false); + + // Set cmx_pub to be a random NoteCommitment + // The proof should fail + let instance_wrong_cmx_pub = Instance { + anchor: instance.anchor, + cv_net: instance.cv_net.clone(), + nf_old: instance.nf_old, + rk: instance.rk.clone(), + cmx: random_note_commitment(&mut rng).into(), + enable_spend: instance.enable_spend, + enable_output: instance.enable_output, + enable_zsa: instance.enable_zsa, + }; + check_proof_of_orchard_circuit(&circuit, &instance_wrong_cmx_pub, false); + + // Set nf_old_pub to be a random Nullifier + // The proof should fail + let instance_wrong_nf_old_pub = Instance { + anchor: instance.anchor, + cv_net: instance.cv_net.clone(), + nf_old: Nullifier::dummy(&mut rng), + rk: instance.rk.clone(), + cmx: instance.cmx, + enable_spend: instance.enable_spend, + enable_output: instance.enable_output, + enable_zsa: instance.enable_zsa, + }; + check_proof_of_orchard_circuit(&circuit, &instance_wrong_nf_old_pub, false); + + // If split_flag = 0 , set psi_nf to be a random Pallas base element + // The proof should fail + if !split_flag { + let circuit_wrong_psi_nf = OrchardCircuitZSA { + path: circuit.path, + pos: circuit.pos, + g_d_old: circuit.g_d_old, + pk_d_old: circuit.pk_d_old, + v_old: circuit.v_old, + rho_old: circuit.rho_old, + psi_old: circuit.psi_old, + rcm_old: circuit.rcm_old.clone(), + cm_old: circuit.cm_old.clone(), + psi_nf: Value::known(pallas::Base::random(&mut rng)), + alpha: circuit.alpha, + ak: circuit.ak.clone(), + nk: circuit.nk, + rivk: circuit.rivk, + g_d_new: circuit.g_d_new, + pk_d_new: circuit.pk_d_new, + v_new: circuit.v_new, + psi_new: circuit.psi_new, + rcm_new: circuit.rcm_new.clone(), + rcv: circuit.rcv, + asset: circuit.asset, + split_flag: circuit.split_flag, + phantom: std::marker::PhantomData, + }; + check_proof_of_orchard_circuit(&circuit_wrong_psi_nf, &instance, false); + } + + // If asset is not equal to the native asset, set enable_zsa = 0 + // The proof should fail + if !is_native_asset { + let instance_wrong_enable_zsa = Instance { + anchor: instance.anchor, + cv_net: instance.cv_net.clone(), + nf_old: instance.nf_old, + rk: instance.rk.clone(), + cmx: instance.cmx, + enable_spend: instance.enable_spend, + enable_output: instance.enable_output, + enable_zsa: false, + }; + check_proof_of_orchard_circuit(&circuit, &instance_wrong_enable_zsa, false); + } + } + } + } +} diff --git a/src/circuit/circuit_zsa/gadget.rs b/src/circuit/circuit_zsa/gadget.rs new file mode 100644 index 000000000..ec5fd4b6a --- /dev/null +++ b/src/circuit/circuit_zsa/gadget.rs @@ -0,0 +1,227 @@ +//! Gadgets used in the Orchard circuit. + +// TODO: try to avoid code duplication with the same module for circuit_vanilla. + +use ff::Field; +use group::Curve; +use pasta_curves::arithmetic::CurveExt; +use pasta_curves::pallas; + +use super::{add_chip, commit_ivk::CommitIvkChip, note_commit::NoteCommitChip, AddInstruction}; +use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}; +use crate::note::AssetBase; +use halo2_gadgets::{ + ecc::{chip::EccChip, chip::EccPoint, EccInstructions, FixedPointBaseField, Point, X}, + poseidon::{ + primitives::{self as poseidon, ConstantLength}, + Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip, + }, + sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}, + utilities::cond_swap::CondSwapChip, +}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Value}, + plonk::{self, Advice, Assigned, Column}, +}; + +impl super::Config { + pub(super) fn add_chip(&self) -> add_chip::AddChip { + add_chip::AddChip::construct(self.add_config.clone()) + } + + pub(super) fn commit_ivk_chip(&self) -> CommitIvkChip { + CommitIvkChip::construct(self.commit_ivk_config.clone()) + } + + pub(super) fn ecc_chip(&self) -> EccChip { + EccChip::construct(self.ecc_config.clone()) + } + + pub(super) fn sinsemilla_chip_1( + &self, + ) -> SinsemillaChip { + SinsemillaChip::construct(self.sinsemilla_config_1.clone()) + } + + pub(super) fn sinsemilla_chip_2( + &self, + ) -> SinsemillaChip { + SinsemillaChip::construct(self.sinsemilla_config_2.clone()) + } + + pub(super) fn merkle_chip_1( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_1.clone()) + } + + pub(super) fn merkle_chip_2( + &self, + ) -> MerkleChip { + MerkleChip::construct(self.merkle_config_2.clone()) + } + + pub(super) fn poseidon_chip(&self) -> PoseidonChip { + PoseidonChip::construct(self.poseidon_config.clone()) + } + + 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 { + NoteCommitChip::construct(self.old_note_commit_config.clone()) + } + + pub(super) fn cond_swap_chip(&self) -> CondSwapChip { + CondSwapChip::construct(self.cond_swap_config.clone()) + } +} + +/// Witnesses the given value in a standalone region. +/// +/// Usages of this helper are technically superfluous, as the single-cell region is only +/// ever used in equality constraints. We could eliminate them with a +/// [write-on-copy abstraction](https://github.com/zcash/halo2/issues/334). +pub(in crate::circuit) fn assign_free_advice( + mut layouter: impl Layouter, + column: Column, + value: Value, +) -> Result, plonk::Error> +where + for<'v> Assigned: From<&'v V>, +{ + layouter.assign_region( + || "load private", + |mut region| region.assign_advice(|| "load private", column, 0, || value), + ) +} + +/// Witnesses is_native_asset. +pub(in crate::circuit) fn assign_is_native_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_native()) { + pallas::Base::one() + } else { + pallas::Base::zero() + } + }), + ) +} + +/// 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() + } + }), + ) +} + +/// `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)] +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, + cond_swap_chip: CondSwapChip, + rho: AssignedCell, + psi: &AssignedCell, + cm: &Point, + nk: AssignedCell, + split_flag: 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.clone(), NullifierK); + 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)?; + + // Add NullifierL to nf + // split_note_nf = NullifierL + nf + let nullifier_l = Point::new_from_constant( + ecc_chip.clone(), + layouter.namespace(|| "witness NullifierL constant"), + pallas::Point::hash_to_curve("z.cash:Orchard")(b"L").to_affine(), + )?; + 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, + cond_swap_chip.mux_on_points( + layouter.namespace(|| "mux on nf"), + &split_flag, + nf.inner(), + split_note_nf.inner(), + )?, + ) + .extract_p()) +} + +pub(in crate::circuit) use super::commit_ivk::gadgets::commit_ivk; +pub(in crate::circuit) use super::note_commit::gadgets::note_commit; +pub(in crate::circuit) use super::value_commit_orchard::gadgets::value_commit_orchard; diff --git a/src/circuit/note_commit.rs b/src/circuit/circuit_zsa/note_commit.rs similarity index 99% rename from src/circuit/note_commit.rs rename to src/circuit/circuit_zsa/note_commit.rs index 47d928fc0..3b1838e20 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/circuit_zsa/note_commit.rs @@ -1,3 +1,5 @@ +// TODO: try to avoid code duplication with the same module for circuit_vanilla. + use core::iter; use group::ff::PrimeField; @@ -2325,7 +2327,7 @@ pub(in crate::circuit) mod gadgets { mod tests { use super::NoteCommitConfig; use crate::{ - circuit::{ + circuit::circuit_zsa::{ gadget::{assign_free_advice, assign_is_native_asset}, note_commit::{gadgets, NoteCommitChip}, }, @@ -2407,7 +2409,7 @@ mod tests { table_idx, meta.lookup_table_column(), meta.lookup_table_column(), - table_range_check_tag, + Some(table_range_check_tag), ); let lagrange_coeffs = [ meta.fixed_column(), @@ -2424,7 +2426,7 @@ mod tests { meta, advices[9], table_idx, - table_range_check_tag, + Some(table_range_check_tag), ); let sinsemilla_config = SinsemillaChip::< OrchardHashDomains, diff --git a/src/circuit/value_commit_orchard.rs b/src/circuit/circuit_zsa/value_commit_orchard.rs similarity index 98% rename from src/circuit/value_commit_orchard.rs rename to src/circuit/circuit_zsa/value_commit_orchard.rs index d9871a5bc..5ccb77a5c 100644 --- a/src/circuit/value_commit_orchard.rs +++ b/src/circuit/circuit_zsa/value_commit_orchard.rs @@ -92,7 +92,7 @@ pub(in crate::circuit) mod gadgets { #[cfg(test)] mod tests { use crate::{ - circuit::gadget::{assign_free_advice, value_commit_orchard}, + circuit::circuit_zsa::gadget::{assign_free_advice, value_commit_orchard}, circuit::K, constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}, note::AssetBase, @@ -174,7 +174,7 @@ mod tests { table_idx, meta.lookup_table_column(), meta.lookup_table_column(), - table_range_check_tag, + Some(table_range_check_tag), ); let lagrange_coeffs = [ @@ -193,7 +193,7 @@ mod tests { meta, advices[9], table_idx, - table_range_check_tag, + Some(table_range_check_tag), ); let sinsemilla_config = SinsemillaChip::configure( diff --git a/src/circuit/commit_ivk.rs b/src/circuit/commit_ivk.rs index 73c24a91f..c9367c5f8 100644 --- a/src/circuit/commit_ivk.rs +++ b/src/circuit/commit_ivk.rs @@ -739,7 +739,7 @@ mod tests { table_idx, meta.lookup_table_column(), meta.lookup_table_column(), - table_range_check_tag, + Some(table_range_check_tag), ); let lagrange_coeffs = [ meta.fixed_column(), @@ -756,7 +756,7 @@ mod tests { meta, advices[9], table_idx, - table_range_check_tag, + Some(table_range_check_tag), ); let sinsemilla_config = SinsemillaChip::< OrchardHashDomains, diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index dff412b72..fb85657ba 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -1,83 +1,12 @@ -//! Gadgets used in the Orchard circuit. - use ff::Field; -use group::Curve; -use pasta_curves::arithmetic::CurveExt; -use pasta_curves::pallas; -use super::{commit_ivk::CommitIvkChip, note_commit::NoteCommitChip}; -use crate::constants::{NullifierK, OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains}; -use crate::note::AssetBase; -use halo2_gadgets::{ - ecc::{chip::EccChip, chip::EccPoint, EccInstructions, FixedPointBaseField, Point, X}, - poseidon::{ - primitives::{self as poseidon, ConstantLength}, - Hash as PoseidonHash, PoseidonSpongeInstructions, Pow5Chip as PoseidonChip, - }, - sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}, - utilities::cond_swap::CondSwapChip, -}; use halo2_proofs::{ - circuit::{AssignedCell, Chip, Layouter, Value}, - plonk::{self, Advice, Assigned, Column}, + circuit::{AssignedCell, Chip, Layouter}, + plonk, }; pub(in crate::circuit) mod add_chip; -impl super::Config { - pub(super) fn add_chip(&self) -> add_chip::AddChip { - add_chip::AddChip::construct(self.add_config.clone()) - } - - pub(super) fn commit_ivk_chip(&self) -> CommitIvkChip { - CommitIvkChip::construct(self.commit_ivk_config.clone()) - } - - pub(super) fn ecc_chip(&self) -> EccChip { - EccChip::construct(self.ecc_config.clone()) - } - - pub(super) fn sinsemilla_chip_1( - &self, - ) -> SinsemillaChip { - SinsemillaChip::construct(self.sinsemilla_config_1.clone()) - } - - pub(super) fn sinsemilla_chip_2( - &self, - ) -> SinsemillaChip { - SinsemillaChip::construct(self.sinsemilla_config_2.clone()) - } - - pub(super) fn merkle_chip_1( - &self, - ) -> MerkleChip { - MerkleChip::construct(self.merkle_config_1.clone()) - } - - pub(super) fn merkle_chip_2( - &self, - ) -> MerkleChip { - MerkleChip::construct(self.merkle_config_2.clone()) - } - - pub(super) fn poseidon_chip(&self) -> PoseidonChip { - PoseidonChip::construct(self.poseidon_config.clone()) - } - - 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 { - NoteCommitChip::construct(self.old_note_commit_config.clone()) - } - - pub(super) fn cond_swap_chip(&self) -> CondSwapChip { - CondSwapChip::construct(self.cond_swap_config.clone()) - } -} - /// An instruction set for adding two circuit words (field elements). pub(in crate::circuit) trait AddInstruction: Chip { /// Constraints `a + b` and returns the sum. @@ -88,151 +17,3 @@ pub(in crate::circuit) trait AddInstruction: Chip { b: &AssignedCell, ) -> Result, plonk::Error>; } - -/// Witnesses the given value in a standalone region. -/// -/// Usages of this helper are technically superfluous, as the single-cell region is only -/// ever used in equality constraints. We could eliminate them with a -/// [write-on-copy abstraction](https://github.com/zcash/halo2/issues/334). -pub(in crate::circuit) fn assign_free_advice( - mut layouter: impl Layouter, - column: Column, - value: Value, -) -> Result, plonk::Error> -where - for<'v> Assigned: From<&'v V>, -{ - layouter.assign_region( - || "load private", - |mut region| region.assign_advice(|| "load private", column, 0, || value), - ) -} - -/// Witnesses is_native_asset. -pub(in crate::circuit) fn assign_is_native_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_native()) { - pallas::Base::one() - } else { - pallas::Base::zero() - } - }), - ) -} - -/// 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() - } - }), - ) -} - -/// `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)] -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, - cond_swap_chip: CondSwapChip, - rho: AssignedCell, - psi: &AssignedCell, - cm: &Point, - nk: AssignedCell, - split_flag: 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.clone(), NullifierK); - 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)?; - - // Add NullifierL to nf - // split_note_nf = NullifierL + nf - let nullifier_l = Point::new_from_constant( - ecc_chip.clone(), - layouter.namespace(|| "witness NullifierL constant"), - pallas::Point::hash_to_curve("z.cash:Orchard")(b"L").to_affine(), - )?; - 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, - cond_swap_chip.mux_on_points( - layouter.namespace(|| "mux on nf"), - &split_flag, - nf.inner(), - split_note_nf.inner(), - )?, - ) - .extract_p()) -} - -pub(in crate::circuit) use crate::circuit::commit_ivk::gadgets::commit_ivk; -pub(in crate::circuit) use crate::circuit::note_commit::gadgets::note_commit; -pub(in crate::circuit) use crate::circuit::value_commit_orchard::gadgets::value_commit_orchard; diff --git a/src/lib.rs b/src/lib.rs index 417116df2..749690ef4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,8 +26,13 @@ pub mod issuance; pub mod keys; pub mod note; pub mod supply_info; -// pub mod note_encryption; // disabled until backward compatability is implemented. -pub mod note_encryption_v3; + +pub mod note_encryption; + +pub use note_encryption::{note_encryption_vanilla, note_encryption_zsa}; + +pub mod orchard_flavors; + pub mod primitives; mod spec; pub mod tree; diff --git a/src/note.rs b/src/note.rs index ca2e83e11..15c31b24b 100644 --- a/src/note.rs +++ b/src/note.rs @@ -8,6 +8,7 @@ use subtle::{Choice, ConditionallySelectable, CtOption}; use crate::{ keys::{EphemeralSecretKey, FullViewingKey, Scope, SpendingKey}, + note_encryption::OrchardDomain, spec::{to_base, to_scalar, NonZeroPallasScalar, PrfExpand}, value::NoteValue, Address, @@ -308,17 +309,17 @@ impl Note { /// An encrypted note. #[derive(Clone)] -pub struct TransmittedNoteCiphertext { +pub struct TransmittedNoteCiphertext { /// The serialization of the ephemeral public key pub epk_bytes: [u8; 32], /// The encrypted note ciphertext - pub enc_ciphertext: [u8; 612], + pub enc_ciphertext: D::NoteCiphertextBytes, /// An encrypted value that allows the holder of the outgoing cipher /// key for the note to recover the note plaintext. pub out_ciphertext: [u8; 80], } -impl fmt::Debug for TransmittedNoteCiphertext { +impl fmt::Debug for TransmittedNoteCiphertext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TransmittedNoteCiphertext") .field("epk_bytes", &self.epk_bytes) diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 6063f9c49..21bbc43da 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -1,108 +1,84 @@ //! In-band secret distribution for Orchard bundles. -use blake2b_simd::{Hash, Params}; -use group::ff::PrimeField; -use std::fmt; -use zcash_note_encryption_zsa::{ - BatchDomain, Domain, EphemeralKeyBytes, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, - AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, -}; +use blake2b_simd::Params; + +use zcash_note_encryption_zsa::{EphemeralKeyBytes, OutgoingCipherKey, ShieldedOutput, MEMO_SIZE}; -use crate::note::AssetBase; use crate::{ action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey, - OutgoingViewingKey, PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret, - }, - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, - value::{NoteValue, ValueCommitment}, + note::{AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed}, Address, Note, }; -const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock"; +use crate::{ + keys::{DiversifiedTransmissionKey, Diversifier, OutgoingViewingKey}, + value::{NoteValue, ValueCommitment}, +}; -/// The size of a v2 compact note. -pub const COMPACT_NOTE_SIZE_V2: usize = 1 + // version - 11 + // diversifier - 8 + // value - 32; // rseed (or rcm prior to ZIP 212) -/// The size of [`NotePlaintextBytes`] for V2. -pub const NOTE_PLAINTEXT_SIZE_V2: usize = COMPACT_NOTE_SIZE_V2 + MEMO_SIZE; -/// The size of an encrypted note plaintext. -pub const ENC_CIPHERTEXT_SIZE_V2: usize = NOTE_PLAINTEXT_SIZE_V2 + AEAD_TAG_SIZE; +pub mod action; +pub mod note_encryption_vanilla; +pub mod note_encryption_zsa; -/// a type to represent the raw bytes of a note plaintext. -#[derive(Clone, Debug)] -pub struct NotePlaintextBytes(pub [u8; NOTE_PLAINTEXT_SIZE_V2]); +mod domain; +mod note_bytes; +mod orchard_domain; -/// a type to represent the raw bytes of an encrypted note plaintext. -#[derive(Clone, Debug)] -pub struct NoteCiphertextBytes(pub [u8; ENC_CIPHERTEXT_SIZE_V2]); +pub use orchard_domain::{OrchardDomain, OrchardDomainBase}; -/// a type to represent the raw bytes of a compact note. -#[derive(Clone, Debug)] -pub struct CompactNotePlaintextBytes(pub [u8; COMPACT_NOTE_SIZE_V2]); +const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock"; -/// a type to represent the raw bytes of an encrypted compact note. -#[derive(Clone, Debug)] -pub struct CompactNoteCiphertextBytes(pub [u8; COMPACT_NOTE_SIZE_V2]); +const NOTE_VERSION_SIZE: usize = 1; +const NOTE_DIVERSIFIER_SIZE: usize = 11; +const NOTE_VALUE_SIZE: usize = 8; +const NOTE_RSEED_SIZE: usize = 32; // rseed (or rcm prior to ZIP 212) -impl AsMut<[u8]> for NotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} +const NOTE_VERSION_OFFSET: usize = 0; +const NOTE_DIVERSIFIER_OFFSET: usize = NOTE_VERSION_OFFSET + NOTE_VERSION_SIZE; +const NOTE_VALUE_OFFSET: usize = NOTE_DIVERSIFIER_OFFSET + NOTE_DIVERSIFIER_SIZE; +const NOTE_RSEED_OFFSET: usize = NOTE_VALUE_OFFSET + NOTE_VALUE_SIZE; -impl From<&[u8]> for NotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - NotePlaintextBytes(s.try_into().unwrap()) - } -} +/// The size of a Vanilla compact note. +const COMPACT_NOTE_SIZE_VANILLA: usize = + NOTE_VERSION_SIZE + NOTE_DIVERSIFIER_SIZE + NOTE_VALUE_SIZE + NOTE_RSEED_SIZE; -impl AsRef<[u8]> for NoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} +/// The size of the encoding of a ZSA asset id. +const ZSA_ASSET_SIZE: usize = 32; -impl From<&[u8]> for NoteCiphertextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - NoteCiphertextBytes(s.try_into().unwrap()) - } -} +/// The size of a ZSA compact note. +const COMPACT_NOTE_SIZE_ZSA: usize = COMPACT_NOTE_SIZE_VANILLA + ZSA_ASSET_SIZE; -impl AsMut<[u8]> for CompactNotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} +type Memo = [u8; MEMO_SIZE]; -impl From<&[u8]> for CompactNotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - CompactNotePlaintextBytes(s.try_into().unwrap()) - } +// FIXME: consider returning enum instead of u8 +/// Retrieves the version of the note plaintext. +/// Returns `Some(u8)` if the version is recognized, otherwise `None`. +fn note_version(plaintext: &[u8]) -> Option { + plaintext.first().and_then(|version| match *version { + 0x02 | 0x03 => Some(*version), + _ => None, + }) } -impl AsRef<[u8]> for CompactNoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } +// Constructs a note plaintext bytes array given note information. +fn build_base_note_plaintext_bytes( + version: u8, + note: &Note, +) -> [u8; NOTE_PLAINTEXT_SIZE] { + let mut np = [0; NOTE_PLAINTEXT_SIZE]; + + np[NOTE_VERSION_OFFSET] = version; + np[NOTE_DIVERSIFIER_OFFSET..NOTE_VALUE_OFFSET] + .copy_from_slice(note.recipient().diversifier().as_array()); + np[NOTE_VALUE_OFFSET..NOTE_RSEED_OFFSET].copy_from_slice(¬e.value().to_bytes()); + np[NOTE_RSEED_OFFSET..COMPACT_NOTE_SIZE_VANILLA].copy_from_slice(note.rseed().as_bytes()); + + np } /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. /// /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs -pub(crate) fn prf_ock_orchard( +fn prf_ock_orchard( ovk: &OutgoingViewingKey, cv: &ValueCommitment, cmx_bytes: &[u8; 32], @@ -124,492 +100,52 @@ pub(crate) fn prf_ock_orchard( ) } -/// note_version will return the version of the note plaintext. -pub fn note_version(plaintext: &[u8]) -> Option { - match plaintext[0] { - 0x02 => Some(0x02), - 0x03 => Some(0x03), - _ => None, - } -} - -fn orchard_parse_note_plaintext_without_memo( - domain: &OrchardDomainV2, - plaintext: &[u8], // TODO: replace with CompactNotePlaintextBytes - get_pk_d: F, +// FIXME: return None for unseccessfull try_into instead of doing unwrap? +/// Parses the note plaintext (excluding the memo) and extracts the note and address if valid. +/// Domain-specific requirements: +/// - If the note version is 3, the `plaintext` must contain a valid encoding of a ZSA asset type. +fn parse_note_plaintext_without_memo, F>( + rho: Nullifier, + plaintext: &Bytes, + get_validated_pk_d: F, ) -> Option<(Note, Address)> where - F: FnOnce(&Diversifier) -> DiversifiedTransmissionKey, + F: FnOnce(&Diversifier) -> Option, { - assert!(plaintext.len() >= COMPACT_NOTE_SIZE_V2); + // The unwraps below are guaranteed to succeed by the assertion above + let diversifier = Diversifier::from_bytes( + plaintext.as_ref()[NOTE_DIVERSIFIER_OFFSET..NOTE_VALUE_OFFSET] + .try_into() + .unwrap(), + ); - // Check note plaintext version - if note_version(plaintext).unwrap() != 0x02 { - return None; - } + let value = NoteValue::from_bytes( + plaintext.as_ref()[NOTE_VALUE_OFFSET..NOTE_RSEED_OFFSET] + .try_into() + .unwrap(), + ); - // The unwraps below are guaranteed to succeed by the assertion above - let diversifier = Diversifier::from_bytes(plaintext[1..12].try_into().unwrap()); - let value = NoteValue::from_bytes(plaintext[12..20].try_into().unwrap()); let rseed = Option::from(RandomSeed::from_bytes( - plaintext[20..COMPACT_NOTE_SIZE_V2].try_into().unwrap(), - &domain.rho, + plaintext.as_ref()[NOTE_RSEED_OFFSET..COMPACT_NOTE_SIZE_VANILLA] + .try_into() + .unwrap(), + &rho, ))?; - let pk_d = get_pk_d(&diversifier); - + let pk_d = get_validated_pk_d(&diversifier)?; let recipient = Address::from_parts(diversifier, pk_d); - let note = Option::from(Note::from_parts( - recipient, - value, - AssetBase::native(), //V2 notes are always native. - domain.rho, - rseed, - ))?; - Some((note, recipient)) -} - -/// Orchard-specific note encryption logic. -#[derive(Debug)] -pub struct OrchardDomainV2 { - rho: Nullifier, -} - -impl memuse::DynamicUsage for OrchardDomainV2 { - fn dynamic_usage(&self) -> usize { - self.rho.dynamic_usage() - } - - fn dynamic_usage_bounds(&self) -> (usize, Option) { - self.rho.dynamic_usage_bounds() - } -} - -impl OrchardDomainV2 { - /// Constructs a domain that can be used to trial-decrypt this action's output note. - pub fn for_action(act: &Action) -> Self { - OrchardDomainV2 { - rho: *act.nullifier(), - } - } - - /// Constructs a domain from a nullifier. - pub fn for_nullifier(nullifier: Nullifier) -> Self { - OrchardDomainV2 { rho: nullifier } - } -} - -impl Domain for OrchardDomainV2 { - type EphemeralSecretKey = EphemeralSecretKey; - type EphemeralPublicKey = EphemeralPublicKey; - type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey; - type SharedSecret = SharedSecret; - type SymmetricKey = Hash; - type Note = Note; - type Recipient = Address; - type DiversifiedTransmissionKey = DiversifiedTransmissionKey; - type IncomingViewingKey = PreparedIncomingViewingKey; - type OutgoingViewingKey = OutgoingViewingKey; - type ValueCommitment = ValueCommitment; - type ExtractedCommitment = ExtractedNoteCommitment; - type ExtractedCommitmentBytes = [u8; 32]; - type Memo = [u8; MEMO_SIZE]; // TODO use a more interesting type - - type NotePlaintextBytes = NotePlaintextBytes; - type NoteCiphertextBytes = NoteCiphertextBytes; - type CompactNotePlaintextBytes = CompactNotePlaintextBytes; - type CompactNoteCiphertextBytes = CompactNoteCiphertextBytes; - - fn derive_esk(note: &Self::Note) -> Option { - Some(note.esk()) - } - - fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey { - *note.recipient().pk_d() - } - - fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { - PreparedEphemeralPublicKey::new(epk) - } - - fn ka_derive_public( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> Self::EphemeralPublicKey { - esk.derive_public(note.recipient().g_d()) - } - - fn ka_agree_enc( - esk: &Self::EphemeralSecretKey, - pk_d: &Self::DiversifiedTransmissionKey, - ) -> Self::SharedSecret { - esk.agree(pk_d) - } - - fn ka_agree_dec( - ivk: &Self::IncomingViewingKey, - epk: &Self::PreparedEphemeralPublicKey, - ) -> Self::SharedSecret { - epk.agree(ivk) - } - - fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey { - secret.kdf_orchard(ephemeral_key) - } - - fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> NotePlaintextBytes { - let mut np = [0; NOTE_PLAINTEXT_SIZE_V2]; - np[0] = 0x02; - np[1..12].copy_from_slice(note.recipient().diversifier().as_array()); - np[12..20].copy_from_slice(¬e.value().to_bytes()); - np[20..52].copy_from_slice(note.rseed().as_bytes()); - np[52..].copy_from_slice(memo); - NotePlaintextBytes(np) - } - - fn derive_ock( - ovk: &Self::OutgoingViewingKey, - cv: &Self::ValueCommitment, - cmstar_bytes: &Self::ExtractedCommitmentBytes, - ephemeral_key: &EphemeralKeyBytes, - ) -> OutgoingCipherKey { - prf_ock_orchard(ovk, cv, cmstar_bytes, ephemeral_key) - } - - fn outgoing_plaintext_bytes( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> OutPlaintextBytes { - let mut op = [0; OUT_PLAINTEXT_SIZE]; - op[..32].copy_from_slice(¬e.recipient().pk_d().to_bytes()); - op[32..].copy_from_slice(&esk.0.to_repr()); - OutPlaintextBytes(op) - } - - fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes { - epk.to_bytes() - } - - fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option { - EphemeralPublicKey::from_bytes(&ephemeral_key.0).into() - } - - fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment { - note.commitment().into() - } - - fn parse_note_plaintext_without_memo_ivk( - &self, - ivk: &Self::IncomingViewingKey, - plaintext: &CompactNotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - DiversifiedTransmissionKey::derive(ivk, diversifier) - }) - } - - fn parse_note_plaintext_without_memo_ovk( - &self, - pk_d: &Self::DiversifiedTransmissionKey, - plaintext: &NotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, &plaintext.0, |_| *pk_d) - } - fn extract_memo( - &self, - plaintext: &NotePlaintextBytes, - ) -> (Self::CompactNotePlaintextBytes, Self::Memo) { - let (compact, memo) = plaintext.0.split_at(COMPACT_NOTE_SIZE_V2); - (compact.try_into().unwrap(), memo.try_into().unwrap()) - } - - fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option { - DiversifiedTransmissionKey::from_bytes(out_plaintext.0[0..32].try_into().unwrap()).into() - } - - fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option { - EphemeralSecretKey::from_bytes(out_plaintext.0[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()) - .into() - } -} - -impl BatchDomain for OrchardDomainV2 { - fn batch_kdf<'a>( - items: impl Iterator, &'a EphemeralKeyBytes)>, - ) -> Vec> { - let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); - - SharedSecret::batch_to_affine(shared_secrets) - .zip(ephemeral_keys.into_iter()) - .map(|(secret, ephemeral_key)| { - secret.map(|dhsecret| SharedSecret::kdf_orchard_inner(dhsecret, ephemeral_key)) - }) - .collect() - } -} - -/// Implementation of in-band secret distribution for Orchard bundles. -pub type OrchardNoteEncryption = zcash_note_encryption_zsa::NoteEncryption; - -impl ShieldedOutput for Action { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.encrypted_note().epk_bytes) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx().to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - Some(NoteCiphertextBytes(self.encrypted_note().enc_ciphertext)) - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - CompactNoteCiphertextBytes( - self.encrypted_note().enc_ciphertext[..COMPACT_NOTE_SIZE_V2] + let asset = match note_version(plaintext.as_ref())? { + 0x02 => AssetBase::native(), + 0x03 => { + let bytes = plaintext.as_ref()[COMPACT_NOTE_SIZE_VANILLA..COMPACT_NOTE_SIZE_ZSA] .try_into() - .unwrap(), - ) - } -} - -/// A compact Action for light clients. -pub struct CompactAction { - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, -} - -impl fmt::Debug for CompactAction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CompactAction") - } -} - -impl From<&Action> for CompactAction -where - Action: ShieldedOutput, -{ - fn from(action: &Action) -> Self { - CompactAction { - nullifier: *action.nullifier(), - cmx: *action.cmx(), - ephemeral_key: action.ephemeral_key(), - enc_ciphertext: CompactNoteCiphertextBytes( - action.encrypted_note().enc_ciphertext[..COMPACT_NOTE_SIZE_V2] - .try_into() - .unwrap(), - ), + .unwrap(); + AssetBase::from_bytes(bytes).unwrap() } - } -} - -impl ShieldedOutput for CompactAction { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.ephemeral_key.0) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx.to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - None - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - self.enc_ciphertext.clone() - } -} - -impl CompactAction { - /// Create a CompactAction from its constituent parts - pub fn from_parts( - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, - ) -> Self { - Self { - nullifier, - cmx, - ephemeral_key, - enc_ciphertext, - } - } - - ///Returns the nullifier of the note being spent. - pub fn nullifier(&self) -> Nullifier { - self.nullifier - } -} - -#[cfg(test)] -mod tests { - use proptest::proptest; - use rand::rngs::OsRng; - use zcash_note_encryption_zsa::{ - try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, Domain, - EphemeralKeyBytes, + _ => panic!("invalid note version"), }; - use super::{prf_ock_orchard, CompactAction, OrchardDomainV2, OrchardNoteEncryption}; - use crate::note::AssetBase; - use crate::{ - action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey, - OutgoingViewingKey, PreparedIncomingViewingKey, - }, - note::{ - testing::arb_native_note, ExtractedNoteCommitment, Nullifier, RandomSeed, - TransmittedNoteCiphertext, - }, - note_encryption::{ - note_version, orchard_parse_note_plaintext_without_memo, NoteCiphertextBytes, - }, - primitives::redpallas, - value::{NoteValue, ValueCommitment}, - Address, Note, - }; - - proptest! { - #[test] - fn test_encoding_roundtrip( - note in arb_native_note(), - ) { - let memo = &crate::test_vectors::note_encryption::test_vectors()[0].memo; - - // Encode. - let mut plaintext = OrchardDomainV2::note_plaintext_bytes(¬e, ¬e.recipient(), memo); - - // Decode. - let domain = OrchardDomainV2 { rho: note.rho() }; - let parsed_version = note_version(plaintext.as_mut()).unwrap(); - let (compact,parsed_memo) = domain.extract_memo(&plaintext); - - let (parsed_note, parsed_recipient) = orchard_parse_note_plaintext_without_memo(&domain, &compact.0, - |diversifier| { - assert_eq!(diversifier, ¬e.recipient().diversifier()); - Some(*note.recipient().pk_d()) - } - ).expect("Plaintext parsing failed"); - - // Check. - assert_eq!(parsed_note, note); - assert_eq!(parsed_recipient, note.recipient()); - assert_eq!(&parsed_memo, memo); - assert_eq!(parsed_version, 0x02); - } - } - - #[test] - fn test_vectors() { - let test_vectors = crate::test_vectors::note_encryption::test_vectors(); - - for tv in test_vectors { - // - // Load the test vector components - // - - // Recipient key material - let ivk = PreparedIncomingViewingKey::new( - &IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(), - ); - let ovk = OutgoingViewingKey::from(tv.ovk); - let d = Diversifier::from_bytes(tv.default_d); - let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap(); - - // Received Action - let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); - let rho = Nullifier::from_bytes(&tv.rho).unwrap(); - let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); - - let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); - let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key); - - // Details about the expected note - let value = NoteValue::from_raw(tv.v); - let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap(); - - // - // Test the individual components - // - - let shared_secret = esk.agree(&pk_d); - assert_eq!(shared_secret.to_bytes(), tv.shared_secret); - - let k_enc = shared_secret.kdf_orchard(&ephemeral_key); - assert_eq!(k_enc.as_bytes(), tv.k_enc); - - let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key); - assert_eq!(ock.as_ref(), tv.ock); - - let recipient = Address::from_parts(d, pk_d); - let note = Note::from_parts(recipient, value, AssetBase::native(), rho, rseed).unwrap(); - assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); - - let action = Action::from_parts( - // rho is the nullifier in the receiving Action. - rho, - // We don't need a valid rk for this test. - redpallas::VerificationKey::dummy(), - cmx, - TransmittedNoteCiphertext { - epk_bytes: ephemeral_key.0, - enc_ciphertext: tv.c_enc, - out_ciphertext: tv.c_out, - }, - cv_net.clone(), - (), - ); - - // - // Test decryption - // (Tested first because it only requires immutable references.) - // - - let domain = OrchardDomainV2 { rho }; - - match try_note_decryption(&domain, &ivk, &action) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Note decryption failed"), - } - - match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) { - Some((decrypted_note, decrypted_to)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - } - None => panic!("Compact note decryption failed"), - } - - match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Output recovery failed"), - } - - // - // Test encryption - // - - let ne = OrchardNoteEncryption::new_with_esk(esk, Some(ovk), note, tv.memo); - - assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]); - assert_eq!( - &ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..], - &tv.c_out[..] - ); - } - } + let note = Option::from(Note::from_parts(recipient, value, asset, rho, rseed))?; + Some((note, recipient)) } diff --git a/src/note_encryption/action.rs b/src/note_encryption/action.rs new file mode 100644 index 000000000..e33e77502 --- /dev/null +++ b/src/note_encryption/action.rs @@ -0,0 +1,95 @@ +//! Defines actions for Orchard shielded outputs and compact action for light clients. + +use std::fmt; + +use super::{ + Action, EphemeralKeyBytes, ExtractedNoteCommitment, Nullifier, OrchardDomain, + OrchardDomainBase, ShieldedOutput, +}; + +impl ShieldedOutput> for Action { + fn ephemeral_key(&self) -> EphemeralKeyBytes { + EphemeralKeyBytes(self.encrypted_note().epk_bytes) + } + + fn cmstar_bytes(&self) -> [u8; 32] { + self.cmx().to_bytes() + } + + fn enc_ciphertext(&self) -> Option { + Some(self.encrypted_note().enc_ciphertext) + } + + // FIXME: split at COMPACT_NOTE_SIZE - is this correct? + fn enc_ciphertext_compact(&self) -> D::CompactNoteCiphertextBytes { + self.encrypted_note().enc_ciphertext.as_ref()[..D::COMPACT_NOTE_SIZE].into() + } +} + +/// A compact Action for light clients. +pub struct CompactAction { + nullifier: Nullifier, + cmx: ExtractedNoteCommitment, + ephemeral_key: EphemeralKeyBytes, + enc_ciphertext: D::CompactNoteCiphertextBytes, +} + +impl fmt::Debug for CompactAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CompactAction") + } +} + +impl From<&Action> for CompactAction +where + Action: ShieldedOutput>, +{ + fn from(action: &Action) -> Self { + CompactAction { + nullifier: *action.nullifier(), + cmx: *action.cmx(), + ephemeral_key: action.ephemeral_key(), + enc_ciphertext: action.enc_ciphertext_compact(), + } + } +} + +impl ShieldedOutput> for CompactAction { + fn ephemeral_key(&self) -> EphemeralKeyBytes { + EphemeralKeyBytes(self.ephemeral_key.0) + } + + fn cmstar_bytes(&self) -> [u8; 32] { + self.cmx.to_bytes() + } + + fn enc_ciphertext(&self) -> Option { + None + } + + fn enc_ciphertext_compact(&self) -> D::CompactNoteCiphertextBytes { + self.enc_ciphertext + } +} + +impl CompactAction { + /// Create a CompactAction from its constituent parts + pub fn from_parts( + nullifier: Nullifier, + cmx: ExtractedNoteCommitment, + ephemeral_key: EphemeralKeyBytes, + enc_ciphertext: D::CompactNoteCiphertextBytes, + ) -> Self { + Self { + nullifier, + cmx, + ephemeral_key, + enc_ciphertext, + } + } + + ///Returns the nullifier of the note being spent. + pub fn nullifier(&self) -> Nullifier { + self.nullifier + } +} diff --git a/src/note_encryption/domain.rs b/src/note_encryption/domain.rs new file mode 100644 index 000000000..56d7055d7 --- /dev/null +++ b/src/note_encryption/domain.rs @@ -0,0 +1,167 @@ +use blake2b_simd::Hash; +use group::ff::PrimeField; + +use zcash_note_encryption_zsa::{ + BatchDomain, Domain, EphemeralKeyBytes, OutPlaintextBytes, OutgoingCipherKey, + OUT_PLAINTEXT_SIZE, +}; + +use crate::{ + keys::{ + DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, + PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret, + }, + note::ExtractedNoteCommitment, + value::ValueCommitment, + Address, Note, +}; + +use super::{ + parse_note_plaintext_without_memo, prf_ock_orchard, Memo, OrchardDomain, OrchardDomainBase, +}; + +impl Domain for OrchardDomainBase { + type EphemeralSecretKey = EphemeralSecretKey; + type EphemeralPublicKey = EphemeralPublicKey; + type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey; + type SharedSecret = SharedSecret; + type SymmetricKey = Hash; + type Note = Note; + type Recipient = Address; + type DiversifiedTransmissionKey = DiversifiedTransmissionKey; + type IncomingViewingKey = PreparedIncomingViewingKey; + type OutgoingViewingKey = OutgoingViewingKey; + type ValueCommitment = ValueCommitment; + type ExtractedCommitment = ExtractedNoteCommitment; + type ExtractedCommitmentBytes = [u8; 32]; + type Memo = Memo; + + type NotePlaintextBytes = D::NotePlaintextBytes; + type NoteCiphertextBytes = D::NoteCiphertextBytes; + type CompactNotePlaintextBytes = D::CompactNotePlaintextBytes; + type CompactNoteCiphertextBytes = D::CompactNoteCiphertextBytes; + + fn derive_esk(note: &Self::Note) -> Option { + Some(note.esk()) + } + + fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey { + *note.recipient().pk_d() + } + + fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { + PreparedEphemeralPublicKey::new(epk) + } + + fn ka_derive_public( + note: &Self::Note, + esk: &Self::EphemeralSecretKey, + ) -> Self::EphemeralPublicKey { + esk.derive_public(note.recipient().g_d()) + } + + fn ka_agree_enc( + esk: &Self::EphemeralSecretKey, + pk_d: &Self::DiversifiedTransmissionKey, + ) -> Self::SharedSecret { + esk.agree(pk_d) + } + + fn ka_agree_dec( + ivk: &Self::IncomingViewingKey, + epk: &Self::PreparedEphemeralPublicKey, + ) -> Self::SharedSecret { + epk.agree(ivk) + } + + fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey { + secret.kdf_orchard(ephemeral_key) + } + + fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> D::NotePlaintextBytes { + D::build_note_plaintext_bytes(note, memo) + } + + fn derive_ock( + ovk: &Self::OutgoingViewingKey, + cv: &Self::ValueCommitment, + cmstar_bytes: &Self::ExtractedCommitmentBytes, + ephemeral_key: &EphemeralKeyBytes, + ) -> OutgoingCipherKey { + prf_ock_orchard(ovk, cv, cmstar_bytes, ephemeral_key) + } + + fn outgoing_plaintext_bytes( + note: &Self::Note, + esk: &Self::EphemeralSecretKey, + ) -> OutPlaintextBytes { + let mut op = [0; OUT_PLAINTEXT_SIZE]; + op[..32].copy_from_slice(¬e.recipient().pk_d().to_bytes()); + op[32..].copy_from_slice(&esk.0.to_repr()); + OutPlaintextBytes(op) + } + + fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes { + epk.to_bytes() + } + + fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option { + EphemeralPublicKey::from_bytes(&ephemeral_key.0).into() + } + + fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment { + note.commitment().into() + } + + fn parse_note_plaintext_without_memo_ivk( + &self, + ivk: &Self::IncomingViewingKey, + plaintext: &D::CompactNotePlaintextBytes, + ) -> Option<(Self::Note, Self::Recipient)> { + parse_note_plaintext_without_memo(self.rho, plaintext, |diversifier| { + Some(DiversifiedTransmissionKey::derive(ivk, diversifier)) + }) + } + + fn parse_note_plaintext_without_memo_ovk( + &self, + pk_d: &Self::DiversifiedTransmissionKey, + plaintext: &D::CompactNotePlaintextBytes, + ) -> Option<(Self::Note, Self::Recipient)> { + parse_note_plaintext_without_memo(self.rho, plaintext, |_| Some(*pk_d)) + } + + // FIXME: consider implementing and using: + // OrchardDomain::split_note_plaintext(plaintext: &Self::NotePlaintextBytes) -> (Self::CompactNotePlaintextBytes, Memo) + fn extract_memo( + &self, + plaintext: &D::NotePlaintextBytes, + ) -> (Self::CompactNotePlaintextBytes, Self::Memo) { + let (compact, memo) = plaintext.as_ref().split_at(D::COMPACT_NOTE_SIZE); + (compact.into(), memo.try_into().unwrap()) + } + + fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option { + DiversifiedTransmissionKey::from_bytes(out_plaintext.0[0..32].try_into().unwrap()).into() + } + + fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option { + EphemeralSecretKey::from_bytes(out_plaintext.0[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()) + .into() + } +} + +impl BatchDomain for OrchardDomainBase { + fn batch_kdf<'a>( + items: impl Iterator, &'a EphemeralKeyBytes)>, + ) -> Vec> { + let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); + + SharedSecret::batch_to_affine(shared_secrets) + .zip(ephemeral_keys) + .map(|(secret, ephemeral_key)| { + secret.map(|dhsecret| SharedSecret::kdf_orchard_inner(dhsecret, ephemeral_key)) + }) + .collect() + } +} diff --git a/src/note_encryption/note_bytes.rs b/src/note_encryption/note_bytes.rs new file mode 100644 index 000000000..4290e6d1a --- /dev/null +++ b/src/note_encryption/note_bytes.rs @@ -0,0 +1,43 @@ +/// Represents a fixed-size array of bytes for note components. +#[derive(Clone, Copy, Debug)] +pub struct NoteBytes(pub [u8; N]); + +impl AsRef<[u8]> for NoteBytes { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for NoteBytes { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// FIXME: consider implementing and using TryFrom instead +impl From<&[u8]> for NoteBytes { + fn from(s: &[u8]) -> Self { + Self(s.try_into().unwrap()) + } +} + +impl From<(&[u8], &[u8])> for NoteBytes { + fn from(s: (&[u8], &[u8])) -> Self { + Self([s.0, s.1].concat().try_into().unwrap()) + } +} + +/// Defines the ability to concatenate two byte slices. +pub trait NoteByteConcat: for<'a> From<(&'a [u8], &'a [u8])> {} + +impl NoteByteConcat for NoteBytes {} + +/// Defines the behavior for types that can provide read-only access to their internal byte array. +pub trait NoteByteReader: AsRef<[u8]> + for<'a> From<&'a [u8]> + Clone + Copy {} + +impl NoteByteReader for NoteBytes {} + +/// Defines the behavior for types that support both read and write access to their internal byte array. +pub trait NoteByteWriter: NoteByteReader + AsMut<[u8]> {} + +impl NoteByteWriter for NoteBytes {} diff --git a/src/note_encryption/note_encryption_vanilla.rs b/src/note_encryption/note_encryption_vanilla.rs new file mode 100644 index 000000000..069a1e3d1 --- /dev/null +++ b/src/note_encryption/note_encryption_vanilla.rs @@ -0,0 +1,209 @@ +// FIXME: consider renaming this to vanilla.rs + +//! In-band secret distribution for Orchard bundles. + +use crate::{orchard_flavors::OrchardVanilla, Note}; + +use super::{ + build_base_note_plaintext_bytes, note_bytes::NoteBytes, Memo, OrchardDomain, + COMPACT_NOTE_SIZE_VANILLA, +}; + +impl OrchardDomain for OrchardVanilla { + const COMPACT_NOTE_SIZE: usize = COMPACT_NOTE_SIZE_VANILLA; + + type NotePlaintextBytes = NoteBytes<{ Self::NOTE_PLAINTEXT_SIZE }>; + type NoteCiphertextBytes = NoteBytes<{ Self::ENC_CIPHERTEXT_SIZE }>; + type CompactNotePlaintextBytes = NoteBytes<{ Self::COMPACT_NOTE_SIZE }>; + type CompactNoteCiphertextBytes = NoteBytes<{ Self::COMPACT_NOTE_SIZE }>; + + fn build_note_plaintext_bytes(note: &Note, memo: &Memo) -> Self::NotePlaintextBytes { + let mut np = build_base_note_plaintext_bytes(0x02, note); + + np[COMPACT_NOTE_SIZE_VANILLA..].copy_from_slice(memo); + + NoteBytes(np) + } +} + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + use rand::rngs::OsRng; + + use zcash_note_encryption_zsa::{ + try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, Domain, + EphemeralKeyBytes, + }; + + use crate::{ + action::Action, + keys::{ + DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey, + OutgoingViewingKey, PreparedIncomingViewingKey, + }, + note::{ + testing::arb_native_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, + TransmittedNoteCiphertext, + }, + primitives::redpallas, + value::{NoteValue, ValueCommitment}, + Address, Note, + }; + + use super::{ + super::{ + action::CompactAction, note_version, parse_note_plaintext_without_memo, + prf_ock_orchard, OrchardDomainBase, + }, + {NoteBytes, OrchardVanilla}, + }; + + type OrchardDomainVanilla = OrchardDomainBase; + + /// Implementation of in-band secret distribution for Orchard bundles. + pub type OrchardNoteEncryptionVanilla = + zcash_note_encryption_zsa::NoteEncryption; + + proptest! { + #[test] + fn test_encoding_roundtrip( + note in arb_native_note(), + ) { + let memo = &crate::test_vectors::note_encryption_vanilla::test_vectors()[0].memo; + let rho = note.rho(); + + // Encode. + let plaintext = OrchardDomainVanilla::note_plaintext_bytes(¬e, memo); + + // Decode. + let domain = OrchardDomainVanilla::for_nullifier(rho); + let parsed_version = note_version(plaintext.as_ref()).unwrap(); + let (compact, parsed_memo) = domain.extract_memo(&plaintext); + + let (parsed_note, parsed_recipient) = parse_note_plaintext_without_memo(rho, &compact, + |diversifier| { + assert_eq!(diversifier, ¬e.recipient().diversifier()); + Some(*note.recipient().pk_d()) + } + ).expect("Plaintext parsing failed"); + + // Check. + assert_eq!(parsed_note, note); + assert_eq!(parsed_recipient, note.recipient()); + assert_eq!(&parsed_memo, memo); + assert_eq!(parsed_version, 0x02); + } + } + + #[test] + fn test_vectors() { + let test_vectors = crate::test_vectors::note_encryption_vanilla::test_vectors(); + + for tv in test_vectors { + // + // Load the test vector components + // + + // Recipient key material + let ivk = PreparedIncomingViewingKey::new( + &IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(), + ); + let ovk = OutgoingViewingKey::from(tv.ovk); + let d = Diversifier::from_bytes(tv.default_d); + let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap(); + + // Received Action + let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); + let rho = Nullifier::from_bytes(&tv.rho).unwrap(); + let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); + + let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); + let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key); + + // Details about the expected note + let value = NoteValue::from_raw(tv.v); + let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap(); + + // + // Test the individual components + // + + let shared_secret = esk.agree(&pk_d); + assert_eq!(shared_secret.to_bytes(), tv.shared_secret); + + let k_enc = shared_secret.kdf_orchard(&ephemeral_key); + assert_eq!(k_enc.as_bytes(), tv.k_enc); + + let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key); + assert_eq!(ock.as_ref(), tv.ock); + + let recipient = Address::from_parts(d, pk_d); + + let asset = AssetBase::native(); + + let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); + assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); + + let action = Action::from_parts( + // rho is the nullifier in the receiving Action. + rho, + // We don't need a valid rk for this test. + redpallas::VerificationKey::dummy(), + cmx, + TransmittedNoteCiphertext { + epk_bytes: ephemeral_key.0, + enc_ciphertext: NoteBytes(tv.c_enc), + out_ciphertext: tv.c_out, + }, + cv_net.clone(), + (), + ); + + // + // Test decryption + // (Tested first because it only requires immutable references.) + // + + let domain = OrchardDomainVanilla::for_nullifier(rho); + + match try_note_decryption(&domain, &ivk, &action) { + Some((decrypted_note, decrypted_to, decrypted_memo)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + assert_eq!(&decrypted_memo[..], &tv.memo[..]); + } + None => panic!("Note decryption failed"), + } + + match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) { + Some((decrypted_note, decrypted_to)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + } + None => panic!("Compact note decryption failed"), + } + + match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) { + Some((decrypted_note, decrypted_to, decrypted_memo)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + assert_eq!(&decrypted_memo[..], &tv.memo[..]); + } + None => panic!("Output recovery failed"), + } + + // + // Test encryption + // + + let ne = OrchardNoteEncryptionVanilla::new_with_esk(esk, Some(ovk), note, tv.memo); + + assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]); + assert_eq!( + &ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..], + &tv.c_out[..] + ); + } + } +} diff --git a/src/note_encryption/note_encryption_zsa.rs b/src/note_encryption/note_encryption_zsa.rs new file mode 100644 index 000000000..d9e557a0b --- /dev/null +++ b/src/note_encryption/note_encryption_zsa.rs @@ -0,0 +1,210 @@ +// FIXME: consider renaming this to zsa.rs + +//! In-band secret distribution for Orchard bundles. + +use crate::{orchard_flavors::OrchardZSA, Note}; + +use super::{ + build_base_note_plaintext_bytes, note_bytes::NoteBytes, Memo, OrchardDomain, + COMPACT_NOTE_SIZE_VANILLA, COMPACT_NOTE_SIZE_ZSA, +}; + +impl OrchardDomain for OrchardZSA { + const COMPACT_NOTE_SIZE: usize = COMPACT_NOTE_SIZE_ZSA; + + type NotePlaintextBytes = NoteBytes<{ Self::NOTE_PLAINTEXT_SIZE }>; + type NoteCiphertextBytes = NoteBytes<{ Self::ENC_CIPHERTEXT_SIZE }>; + type CompactNotePlaintextBytes = NoteBytes<{ Self::COMPACT_NOTE_SIZE }>; + type CompactNoteCiphertextBytes = NoteBytes<{ Self::COMPACT_NOTE_SIZE }>; + + fn build_note_plaintext_bytes(note: &Note, memo: &Memo) -> Self::NotePlaintextBytes { + let mut np = build_base_note_plaintext_bytes(0x03, note); + + np[COMPACT_NOTE_SIZE_VANILLA..COMPACT_NOTE_SIZE_ZSA] + .copy_from_slice(¬e.asset().to_bytes()); + np[COMPACT_NOTE_SIZE_ZSA..].copy_from_slice(memo); + + NoteBytes(np) + } +} + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + use rand::rngs::OsRng; + + use zcash_note_encryption_zsa::{ + try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, Domain, + EphemeralKeyBytes, + }; + + use crate::{ + action::Action, + keys::{ + DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey, + OutgoingViewingKey, PreparedIncomingViewingKey, + }, + note::{ + testing::arb_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, + TransmittedNoteCiphertext, + }, + primitives::redpallas, + value::{NoteValue, ValueCommitment}, + Address, Note, + }; + + use super::{ + super::{ + action::CompactAction, note_version, parse_note_plaintext_without_memo, + prf_ock_orchard, OrchardDomainBase, + }, + NoteBytes, OrchardZSA, + }; + + type OrchardDomainZSA = OrchardDomainBase; + + /// Implementation of in-band secret distribution for Orchard bundles. + pub type OrchardNoteEncryptionZSA = zcash_note_encryption_zsa::NoteEncryption; + + proptest! { + #[test] + fn test_encoding_roundtrip( + note in arb_note(NoteValue::from_raw(100)), + ) { + let memo = &crate::test_vectors::note_encryption_zsa::test_vectors()[0].memo; + let rho = note.rho(); + + // Encode. + let plaintext = OrchardDomainZSA::note_plaintext_bytes(¬e, memo); + + // Decode. + let domain = OrchardDomainZSA::for_nullifier(rho); + let parsed_version = note_version(plaintext.as_ref()).unwrap(); + let (compact, parsed_memo) = domain.extract_memo(&plaintext); + + let (parsed_note, parsed_recipient) = parse_note_plaintext_without_memo(rho, &compact, + |diversifier| { + assert_eq!(diversifier, ¬e.recipient().diversifier()); + Some(*note.recipient().pk_d()) + } + ).expect("Plaintext parsing failed"); + + // Check. + assert_eq!(parsed_note, note); + assert_eq!(parsed_recipient, note.recipient()); + assert_eq!(&parsed_memo, memo); + assert_eq!(parsed_version, 0x03); + } + } + + #[test] + fn test_vectors() { + let test_vectors = crate::test_vectors::note_encryption_zsa::test_vectors(); + + for tv in test_vectors { + // + // Load the test vector components + // + + // Recipient key material + let ivk = PreparedIncomingViewingKey::new( + &IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(), + ); + let ovk = OutgoingViewingKey::from(tv.ovk); + let d = Diversifier::from_bytes(tv.default_d); + let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap(); + + // Received Action + let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); + let rho = Nullifier::from_bytes(&tv.rho).unwrap(); + let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); + + let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); + let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key); + + // Details about the expected note + let value = NoteValue::from_raw(tv.v); + let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap(); + + // + // Test the individual components + // + + let shared_secret = esk.agree(&pk_d); + assert_eq!(shared_secret.to_bytes(), tv.shared_secret); + + let k_enc = shared_secret.kdf_orchard(&ephemeral_key); + assert_eq!(k_enc.as_bytes(), tv.k_enc); + + let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key); + assert_eq!(ock.as_ref(), tv.ock); + + let recipient = Address::from_parts(d, pk_d); + + let asset = AssetBase::from_bytes(&tv.asset).unwrap(); + + let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); + assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); + + let action = Action::from_parts( + // rho is the nullifier in the receiving Action. + rho, + // We don't need a valid rk for this test. + redpallas::VerificationKey::dummy(), + cmx, + TransmittedNoteCiphertext { + epk_bytes: ephemeral_key.0, + enc_ciphertext: NoteBytes(tv.c_enc), + out_ciphertext: tv.c_out, + }, + cv_net.clone(), + (), + ); + + // + // Test decryption + // (Tested first because it only requires immutable references.) + // + + let domain = OrchardDomainZSA::for_nullifier(rho); + + match try_note_decryption(&domain, &ivk, &action) { + Some((decrypted_note, decrypted_to, decrypted_memo)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + assert_eq!(&decrypted_memo[..], &tv.memo[..]); + } + None => panic!("Note decryption failed"), + } + + match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) { + Some((decrypted_note, decrypted_to)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + } + None => panic!("Compact note decryption failed"), + } + + match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) { + Some((decrypted_note, decrypted_to, decrypted_memo)) => { + assert_eq!(decrypted_note, note); + assert_eq!(decrypted_to, recipient); + assert_eq!(&decrypted_memo[..], &tv.memo[..]); + } + None => panic!("Output recovery failed"), + } + + // + // Test encryption + // + + let ne = OrchardNoteEncryptionZSA::new_with_esk(esk, Some(ovk), note, tv.memo); + + assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]); + assert_eq!( + &ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..], + &tv.c_out[..] + ); + } + } +} diff --git a/src/note_encryption/orchard_domain.rs b/src/note_encryption/orchard_domain.rs new file mode 100644 index 000000000..765781d07 --- /dev/null +++ b/src/note_encryption/orchard_domain.rs @@ -0,0 +1,61 @@ +use core::fmt; + +use zcash_note_encryption_zsa::{AEAD_TAG_SIZE, MEMO_SIZE}; + +use crate::{action::Action, note::Nullifier, Note}; + +use super::{ + note_bytes::{NoteByteConcat, NoteByteReader, NoteByteWriter}, + Memo, +}; + +/// Represents the Orchard protocol domain specifics required for note encryption and decryption. +pub trait OrchardDomain: fmt::Debug + Clone { + /// The size of a compact note, specific to the Orchard protocol. + // 52 for Vanuilla, 84 for ZSA + const COMPACT_NOTE_SIZE: usize; + + /// The size of a note plaintext, including memo and other metadata. + // + 512 (564 for Vanilla, 596 for ZSA) + const NOTE_PLAINTEXT_SIZE: usize = Self::COMPACT_NOTE_SIZE + MEMO_SIZE; + + /// The size of an encrypted note ciphertext, accounting for additional AEAD tag space. + // + 16 (580 for Vanilla, 612 for ZSA) + const ENC_CIPHERTEXT_SIZE: usize = Self::NOTE_PLAINTEXT_SIZE + AEAD_TAG_SIZE; + + /// A type to represent the raw bytes of a note plaintext. + type NotePlaintextBytes: NoteByteWriter; + /// A type to represent the raw bytes of an encrypted note plaintext. + type NoteCiphertextBytes: NoteByteWriter + NoteByteConcat; + /// A type to represent the raw bytes of a compact note. + type CompactNotePlaintextBytes: NoteByteWriter; + /// A type to represent the raw bytes of an encrypted compact note. + type CompactNoteCiphertextBytes: NoteByteReader; + + /// Builds NotePlaintextBytes from Note and Memo. + fn build_note_plaintext_bytes(note: &Note, memo: &Memo) -> Self::NotePlaintextBytes; +} + +/// Orchard-specific note encryption logic. +#[derive(Debug, Clone)] +pub struct OrchardDomainBase { + /// Represents a nullifier which is used to prevent double spending within the Orchard protocol. + pub rho: Nullifier, + phantom: std::marker::PhantomData, +} + +impl OrchardDomainBase { + /// Constructs a domain that can be used to trial-decrypt this action's output note. + pub fn for_action(act: &Action) -> Self { + Self::for_nullifier(*act.nullifier()) + } + + /// Constructs a domain from a nullifier. + // FIXME: is this used only in tests? + pub fn for_nullifier(nullifier: Nullifier) -> Self { + Self { + rho: nullifier, + phantom: Default::default(), + } + } +} diff --git a/src/note_encryption_v2v3.rs b/src/note_encryption_v2v3.rs deleted file mode 100644 index 107184ab8..000000000 --- a/src/note_encryption_v2v3.rs +++ /dev/null @@ -1,716 +0,0 @@ -//! In-band secret distribution for Orchard bundles. - -use blake2b_simd::{Hash, Params}; -use core::fmt; -use group::ff::PrimeField; -use zcash_note_encryption_zsa::{ - BatchDomain, Domain, EphemeralKeyBytes, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, - AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, -}; - -use crate::note::AssetBase; -use crate::{ - action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey, - IncomingViewingKey, OutgoingViewingKey, SharedSecret, - }, - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, - spec::diversify_hash, - value::{NoteValue, ValueCommitment}, - Address, Note, -}; - -const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock"; - -/// The size of a v2 compact note. -pub const COMPACT_NOTE_SIZE_V2: usize = 1 + // version - 11 + // diversifier - 8 + // value - 32; // rseed (or rcm prior to ZIP 212) -/// The size of [`NotePlaintextBytes`] for V2. -pub const NOTE_PLAINTEXT_SIZE_V2: usize = COMPACT_NOTE_SIZE_V2 + MEMO_SIZE; -/// The size of an encrypted note plaintext. -pub const ENC_CIPHERTEXT_SIZE_V2: usize = NOTE_PLAINTEXT_SIZE_V2 + AEAD_TAG_SIZE; - -/// The size of the encoding of a ZSA asset id. -const ZSA_ASSET_SIZE: usize = 32; -/// The size of a v3 compact note. -const COMPACT_NOTE_SIZE_V3: usize = COMPACT_NOTE_SIZE_V2 + ZSA_ASSET_SIZE; -/// The size of [`NotePlaintextBytes`] for V3. -const NOTE_PLAINTEXT_SIZE_V3: usize = COMPACT_NOTE_SIZE_V3 + MEMO_SIZE; -/// The size of the encrypted ciphertext of the ZSA variant of a note. -const ENC_CIPHERTEXT_SIZE_V3: usize = NOTE_PLAINTEXT_SIZE_V3 + AEAD_TAG_SIZE; - -/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. -/// -/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs -pub(crate) fn prf_ock_orchard( - ovk: &OutgoingViewingKey, - cv: &ValueCommitment, - cmx_bytes: &[u8; 32], - ephemeral_key: &EphemeralKeyBytes, -) -> OutgoingCipherKey { - OutgoingCipherKey( - Params::new() - .hash_length(32) - .personal(PRF_OCK_ORCHARD_PERSONALIZATION) - .to_state() - .update(ovk.as_ref()) - .update(&cv.to_bytes()) - .update(cmx_bytes) - .update(ephemeral_key.as_ref()) - .finalize() - .as_bytes() - .try_into() - .unwrap(), - ) -} - -// TODO: VA: Needs updating -/// Domain-specific requirements: -/// - If the note version is 3, the `plaintext` must contain a valid encoding of a ZSA asset type. -fn orchard_parse_note_plaintext_without_memo( - domain: &OrchardDomain, - plaintext: &CompactNotePlaintextBytes, - get_validated_pk_d: F, -) -> Option<(Note, Address)> -where - F: FnOnce(&Diversifier) -> Option, -{ - // Check note plaintext version - // and parse the asset type accordingly. - let asset = parse_version_and_asset_type(plaintext)?; - - let mut plaintext_inner = [0u8; COMPACT_NOTE_SIZE_V2]; - match plaintext { - CompactNotePlaintextBytes::V2(x) => { - plaintext_inner.copy_from_slice(&x[..COMPACT_NOTE_SIZE_V2]) - } - CompactNotePlaintextBytes::V3(x) => { - plaintext_inner.copy_from_slice(&x[..COMPACT_NOTE_SIZE_V2]) - } - } - - // The unwraps below are guaranteed to succeed by the assertion above - let diversifier = Diversifier::from_bytes(plaintext_inner[1..12].try_into().unwrap()); - let value = NoteValue::from_bytes(plaintext_inner[12..20].try_into().unwrap()); - let rseed = Option::from(RandomSeed::from_bytes( - plaintext_inner[20..COMPACT_NOTE_SIZE_V2] - .try_into() - .unwrap(), - &domain.rho, - ))?; - - let pk_d = get_validated_pk_d(&diversifier)?; - - let recipient = Address::from_parts(diversifier, pk_d); - let note = Option::from(Note::from_parts(recipient, value, asset, domain.rho, rseed))?; - Some((note, recipient)) -} - -fn parse_version_and_asset_type(plaintext: &CompactNotePlaintextBytes) -> Option { - match plaintext { - CompactNotePlaintextBytes::V2(x) if x[0] == 0x02 => Some(AssetBase::native()), - CompactNotePlaintextBytes::V3(x) if x[0] == 0x03 => { - let bytes = x[COMPACT_NOTE_SIZE_V2..COMPACT_NOTE_SIZE_V3] - .try_into() - .unwrap(); - AssetBase::from_bytes(bytes).into() - } - _ => None, - } -} - -/// Orchard-specific note encryption logic. -#[derive(Debug)] -pub struct OrchardDomain { - rho: Nullifier, -} - -/// Newtype for encoding the note plaintext post ZSA. -// pub struct NotePlaintextZSA (pub [u8; ZSA_NOTE_PLAINTEXT_SIZE]); -#[derive(Clone, Debug)] -pub enum NotePlaintextBytes { - /// Variant for old note plaintexts. - V2([u8; NOTE_PLAINTEXT_SIZE_V2]), - /// Variant for the new note plaintexts post ZSA. - V3([u8; NOTE_PLAINTEXT_SIZE_V3]), -} - -impl AsMut<[u8]> for NotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - match self { - NotePlaintextBytes::V2(x) => x.as_mut(), - NotePlaintextBytes::V3(x) => x.as_mut(), - } - } -} - -impl From<&[u8]> for NotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - match s.len() { - NOTE_PLAINTEXT_SIZE_V2 => NotePlaintextBytes::V2(s.try_into().unwrap()), - NOTE_PLAINTEXT_SIZE_V3 => NotePlaintextBytes::V3(s.try_into().unwrap()), - _ => panic!("Invalid note plaintext size"), - } - } -} - -/// Newtype for encoding the encrypted note ciphertext post ZSA. -// pub struct EncNoteCiphertextZSA (pub [u8; ZSA_ENC_CIPHERTEXT_SIZE]); -#[derive(Clone, Debug)] -pub enum NoteCiphertextBytes { - /// Variant for old encrypted note ciphertexts. - V2([u8; ENC_CIPHERTEXT_SIZE_V2]), - /// Variant for new encrypted note ciphertexts post ZSA. - V3([u8; ENC_CIPHERTEXT_SIZE_V3]), -} - -impl AsRef<[u8]> for NoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - match self { - NoteCiphertextBytes::V2(x) => x, - NoteCiphertextBytes::V3(x) => x, - } - } -} - -/// Panics if the given slice is not `ENC_CIPHERTEXT_SIZE_V2` or `ENC_CIPHERTEXT_SIZE_V3` bytes long. -impl From<&[u8]> for NoteCiphertextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - match s.len() { - ENC_CIPHERTEXT_SIZE_V2 => NoteCiphertextBytes::V2(s.try_into().unwrap()), - ENC_CIPHERTEXT_SIZE_V3 => NoteCiphertextBytes::V3(s.try_into().unwrap()), - _ => panic!("Invalid length for compact note plaintext"), - } - } -} - -/// Newtype for encoding a compact note -#[derive(Clone, Debug)] -pub enum CompactNotePlaintextBytes { - /// Variant for old compact notes. - V2([u8; COMPACT_NOTE_SIZE_V2]), - /// Variant for new compact notes post ZSA. - V3([u8; COMPACT_NOTE_SIZE_V3]), -} - -impl AsMut<[u8]> for CompactNotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - match self { - CompactNotePlaintextBytes::V2(x) => x, - CompactNotePlaintextBytes::V3(x) => x, - } - } -} - -/// Panics if the given slice is not `COMPACT_NOTE_SIZE_V2` or `COMPACT_NOTE_SIZE_V3` bytes long. -impl From<&[u8]> for CompactNotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - match s.len() { - COMPACT_NOTE_SIZE_V2 => CompactNotePlaintextBytes::V2(s.try_into().unwrap()), - COMPACT_NOTE_SIZE_V3 => CompactNotePlaintextBytes::V3(s.try_into().unwrap()), - _ => panic!("Invalid length for compact note plaintext"), - } - } -} - -/// Newtype for encoding a compact note -#[derive(Clone, Debug)] -pub enum CompactNoteCiphertextBytes { - /// Variant for old compact notes. - V2([u8; COMPACT_NOTE_SIZE_V2]), - /// Variant for new compact notes post ZSA. - V3([u8; COMPACT_NOTE_SIZE_V3]), -} - -impl AsRef<[u8]> for CompactNoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - match self { - CompactNoteCiphertextBytes::V2(x) => x, - CompactNoteCiphertextBytes::V3(x) => x, - } - } -} - -impl OrchardDomain { - /// Constructs a domain that can be used to trial-decrypt this action's output note. - pub fn for_action(act: &Action) -> Self { - OrchardDomain { - rho: *act.nullifier(), - } - } - - /// Constructs a domain from a nullifier. - pub fn for_nullifier(nullifier: Nullifier) -> Self { - OrchardDomain { rho: nullifier } - } -} - -impl Domain for OrchardDomain { - type EphemeralSecretKey = EphemeralSecretKey; - type EphemeralPublicKey = EphemeralPublicKey; - type PreparedEphemeralPublicKey = EphemeralPublicKey; - type SharedSecret = SharedSecret; - type SymmetricKey = Hash; - type Note = Note; - type Recipient = Address; - type DiversifiedTransmissionKey = DiversifiedTransmissionKey; - type IncomingViewingKey = IncomingViewingKey; - type OutgoingViewingKey = OutgoingViewingKey; - type ValueCommitment = ValueCommitment; - type ExtractedCommitment = ExtractedNoteCommitment; - type ExtractedCommitmentBytes = [u8; 32]; - type Memo = [u8; MEMO_SIZE]; // TODO use a more interesting type - - type NotePlaintextBytes = NotePlaintextBytes; - type NoteCiphertextBytes = NoteCiphertextBytes; - type CompactNotePlaintextBytes = CompactNotePlaintextBytes; - type CompactNoteCiphertextBytes = CompactNoteCiphertextBytes; - - fn derive_esk(note: &Self::Note) -> Option { - Some(note.esk()) - } - - fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey { - *note.recipient().pk_d() - } - - fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { - epk - } - - fn ka_derive_public( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> Self::EphemeralPublicKey { - esk.derive_public(note.recipient().g_d()) - } - - fn ka_agree_enc( - esk: &Self::EphemeralSecretKey, - pk_d: &Self::DiversifiedTransmissionKey, - ) -> Self::SharedSecret { - esk.agree(pk_d) - } - - fn ka_agree_dec( - ivk: &Self::IncomingViewingKey, - epk: &Self::PreparedEphemeralPublicKey, - ) -> Self::SharedSecret { - epk.agree(ivk) - } - - fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey { - secret.kdf_orchard(ephemeral_key) - } - - fn note_plaintext_bytes( - note: &Self::Note, - _: &Self::Recipient, - memo: &Self::Memo, - ) -> NotePlaintextBytes { - let mut np = [0u8; NOTE_PLAINTEXT_SIZE_V3]; - np[0] = 0x03; - np[1..12].copy_from_slice(note.recipient().diversifier().as_array()); - np[12..20].copy_from_slice(¬e.value().to_bytes()); - np[20..52].copy_from_slice(note.rseed().as_bytes()); - let zsa_type = note.asset().to_bytes(); - np[52..84].copy_from_slice(&zsa_type); - np[84..].copy_from_slice(memo); - NotePlaintextBytes::V3(np) - } - - fn derive_ock( - ovk: &Self::OutgoingViewingKey, - cv: &Self::ValueCommitment, - cmstar_bytes: &Self::ExtractedCommitmentBytes, - ephemeral_key: &EphemeralKeyBytes, - ) -> OutgoingCipherKey { - prf_ock_orchard(ovk, cv, cmstar_bytes, ephemeral_key) - } - - fn outgoing_plaintext_bytes( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> OutPlaintextBytes { - let mut op = [0; OUT_PLAINTEXT_SIZE]; - op[..32].copy_from_slice(¬e.recipient().pk_d().to_bytes()); - op[32..].copy_from_slice(&esk.0.to_repr()); - OutPlaintextBytes(op) - } - - fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes { - epk.to_bytes() - } - - fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option { - EphemeralPublicKey::from_bytes(&ephemeral_key.0).into() - } - - fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment { - note.commitment().into() - } - - fn parse_note_plaintext_without_memo_ivk( - &self, - ivk: &Self::IncomingViewingKey, - plaintext: &CompactNotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - Some(DiversifiedTransmissionKey::derive(ivk, diversifier)) - }) - } - - fn parse_note_plaintext_without_memo_ovk( - &self, - pk_d: &Self::DiversifiedTransmissionKey, - esk: &Self::EphemeralSecretKey, - ephemeral_key: &EphemeralKeyBytes, - plaintext: &CompactNotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - if esk - .derive_public(diversify_hash(diversifier.as_array())) - .to_bytes() - .0 - == ephemeral_key.0 - { - Some(*pk_d) - } else { - None - } - }) - } - - fn extract_memo( - &self, - plaintext: &NotePlaintextBytes, - ) -> (Self::CompactNotePlaintextBytes, Self::Memo) { - match plaintext { - NotePlaintextBytes::V2(np) => { - let (compact, memo) = np.split_at(COMPACT_NOTE_SIZE_V2); - ( - CompactNotePlaintextBytes::V2(compact.try_into().unwrap()), - memo.try_into().unwrap(), - ) - } - NotePlaintextBytes::V3(np) => { - let (compact, memo) = np.split_at(COMPACT_NOTE_SIZE_V3); - ( - CompactNotePlaintextBytes::V3(compact.try_into().unwrap()), - memo.try_into().unwrap(), - ) - } - } - } - - fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option { - DiversifiedTransmissionKey::from_bytes(out_plaintext.0[0..32].try_into().unwrap()).into() - } - - fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option { - EphemeralSecretKey::from_bytes(out_plaintext.0[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()) - .into() - } -} - -impl BatchDomain for OrchardDomain { - fn batch_kdf<'a>( - items: impl Iterator, &'a EphemeralKeyBytes)>, - ) -> Vec> { - let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); - - SharedSecret::batch_to_affine(shared_secrets) - .zip(ephemeral_keys.into_iter()) - .map(|(secret, ephemeral_key)| { - secret.map(|dhsecret| SharedSecret::kdf_orchard_inner(dhsecret, ephemeral_key)) - }) - .collect() - } -} - -/// Implementation of in-band secret distribution for Orchard bundles. -pub type OrchardNoteEncryption = zcash_note_encryption_zsa::NoteEncryption; - -impl ShieldedOutput for Action { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.encrypted_note().epk_bytes) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx().to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - let result = self.encrypted_note().enc_ciphertext.clone(); - Some(result) - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - match self.encrypted_note().enc_ciphertext { - NoteCiphertextBytes::V2(ncx) => { - CompactNoteCiphertextBytes::V2(ncx[..COMPACT_NOTE_SIZE_V2].try_into().unwrap()) - } - NoteCiphertextBytes::V3(ncx) => { - CompactNoteCiphertextBytes::V3(ncx[..COMPACT_NOTE_SIZE_V3].try_into().unwrap()) - } - } - } -} - -/// A compact Action for light clients. -pub struct CompactAction { - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, -} - -impl fmt::Debug for CompactAction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CompactAction") - } -} - -impl From<&Action> for CompactAction { - fn from(action: &Action) -> Self { - let comp_ciphertext: CompactNoteCiphertextBytes = - match action.encrypted_note().enc_ciphertext { - NoteCiphertextBytes::V2(ncx) => { - CompactNoteCiphertextBytes::V2(ncx[..COMPACT_NOTE_SIZE_V2].try_into().unwrap()) - } - NoteCiphertextBytes::V3(ncx) => { - CompactNoteCiphertextBytes::V3(ncx[..COMPACT_NOTE_SIZE_V3].try_into().unwrap()) - } - }; - CompactAction { - nullifier: *action.nullifier(), - cmx: *action.cmx(), - ephemeral_key: action.ephemeral_key(), - enc_ciphertext: comp_ciphertext, - } - } -} - -impl ShieldedOutput for CompactAction { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.ephemeral_key.0) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx.to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - None - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - &self.enc_ciphertext - } -} - -impl CompactAction { - /// Create a CompactAction from its constituent parts - pub fn from_parts( - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, - ) -> Self { - Self { - nullifier, - cmx, - ephemeral_key, - enc_ciphertext, - } - } - - ///Returns the nullifier of the note being spent. - pub fn nullifier(&self) -> Nullifier { - self.nullifier - } -} - -#[cfg(test)] -mod tests { - use proptest::prelude::*; - use rand::rngs::OsRng; - use zcash_note_encryption_zsa::{ - try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, Domain, - EphemeralKeyBytes, - }; - - use super::{prf_ock_orchard, CompactAction, OrchardDomain, OrchardNoteEncryption}; - use crate::note::AssetBase; - use crate::note_encryption::NoteCiphertextBytes; - use crate::{ - action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey, - OutgoingViewingKey, - }, - note::{ - testing::arb_note, ExtractedNoteCommitment, Nullifier, RandomSeed, - TransmittedNoteCiphertext, - }, - primitives::redpallas, - value::{NoteValue, ValueCommitment}, - Address, Note, - }; - - use super::{orchard_parse_note_plaintext_without_memo, version}; - - proptest! { - #[test] - fn test_encoding_roundtrip( - note in arb_note(NoteValue::from_raw(100)), - ) { - let memo = &crate::test_vectors::note_encryption::test_vectors()[0].memo; - - // Encode. - let mut plaintext = OrchardDomain::note_plaintext_bytes(¬e, ¬e.recipient(), memo); - - // Decode. - let domain = OrchardDomain { rho: note.rho() }; - let parsed_version = version(plaintext.as_mut()).unwrap(); - let (mut compact,parsed_memo) = domain.extract_memo(&plaintext); - - let (parsed_note, parsed_recipient) = orchard_parse_note_plaintext_without_memo(&domain, &compact.as_mut(), - |diversifier| { - assert_eq!(diversifier, ¬e.recipient().diversifier()); - Some(*note.recipient().pk_d()) - } - ).expect("Plaintext parsing failed"); - - // Check. - assert_eq!(parsed_note, note); - assert_eq!(parsed_recipient, note.recipient()); - assert_eq!(&parsed_memo, memo); - assert_eq!(parsed_version, 0x03); - } - } - - #[test] - fn test_vectors() { - let test_vectors = crate::test_vectors::note_encryption::test_vectors(); - - for tv in test_vectors { - // - // Load the test vector components - // - - // Recipient key material - let ivk = IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(); - let ovk = OutgoingViewingKey::from(tv.ovk); - let d = Diversifier::from_bytes(tv.default_d); - let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap(); - - // Received Action - let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); - let rho = Nullifier::from_bytes(&tv.rho).unwrap(); - let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); - - let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); - let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key); - - // Details about the expected note - let value = NoteValue::from_raw(tv.v); - let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap(); - - // - // Test the individual components - // - - let shared_secret = esk.agree(&pk_d); - assert_eq!(shared_secret.to_bytes(), tv.shared_secret); - - let k_enc = shared_secret.kdf_orchard(&ephemeral_key); - assert_eq!(k_enc.as_bytes(), tv.k_enc); - - let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key); - assert_eq!(ock.as_ref(), tv.ock); - - let recipient = Address::from_parts(d, pk_d); - - let asset = match tv.asset { - None => AssetBase::native(), - Some(type_bytes) => AssetBase::from_bytes(&type_bytes).unwrap(), - }; - - let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); - assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); - - let action = Action::from_parts( - // rho is the nullifier in the receiving Action. - rho, - // We don't need a valid rk for this test. - redpallas::VerificationKey::dummy(), - cmx, - TransmittedNoteCiphertext { - epk_bytes: ephemeral_key.0, - enc_ciphertext: NoteCiphertextBytes::V3(tv.c_enc), // TODO: VA: Would need a mix of V2 and V3 eventually - out_ciphertext: tv.c_out, - }, - cv_net.clone(), - (), - ); - - // - // Test decryption - // (Tested first because it only requires immutable references.) - // - - let domain = OrchardDomain { rho }; - - match try_note_decryption(&domain, &ivk, &action) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Note decryption failed"), - } - - match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) { - Some((decrypted_note, decrypted_to)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - } - None => panic!("Compact note decryption failed"), - } - - match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Output recovery failed"), - } - - // - // Test encryption - // - - let ne = OrchardNoteEncryption::new_with_esk(esk, Some(ovk), note, recipient, tv.memo); - - // assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]); - assert_eq!( - &ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..], - &tv.c_out[..] - ); - } - } -} diff --git a/src/note_encryption_v3.rs b/src/note_encryption_v3.rs deleted file mode 100644 index ceff0cbe2..000000000 --- a/src/note_encryption_v3.rs +++ /dev/null @@ -1,616 +0,0 @@ -//! In-band secret distribution for Orchard bundles. - -use blake2b_simd::{Hash, Params}; -use core::fmt; -use group::ff::PrimeField; -use zcash_note_encryption_zsa::{ - BatchDomain, Domain, EphemeralKeyBytes, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, - AEAD_TAG_SIZE, MEMO_SIZE, OUT_PLAINTEXT_SIZE, -}; - -use crate::note::AssetBase; -use crate::{ - action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey, - OutgoingViewingKey, PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret, - }, - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, - value::{NoteValue, ValueCommitment}, - Address, Note, -}; - -const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock"; - -/// The size of a v2 compact note. -pub const COMPACT_NOTE_SIZE_V2: usize = 1 + // version - 11 + // diversifier - 8 + // value - 32; // rseed (or rcm prior to ZIP 212) -/// The size of [`NotePlaintextBytes`] for V2. - -/// The size of the encoding of a ZSA asset id. -const ZSA_ASSET_SIZE: usize = 32; -/// The size of a v3 compact note. -pub const COMPACT_NOTE_SIZE_V3: usize = COMPACT_NOTE_SIZE_V2 + ZSA_ASSET_SIZE; -/// The size of [`NotePlaintextBytes`] for V3. -pub const NOTE_PLAINTEXT_SIZE_V3: usize = COMPACT_NOTE_SIZE_V3 + MEMO_SIZE; -/// The size of the encrypted ciphertext of the ZSA variant of a note. -pub const ENC_CIPHERTEXT_SIZE_V3: usize = NOTE_PLAINTEXT_SIZE_V3 + AEAD_TAG_SIZE; - -/// a type to represent the raw bytes of a note plaintext. -#[derive(Clone, Debug)] -pub struct NotePlaintextBytes(pub [u8; NOTE_PLAINTEXT_SIZE_V3]); - -/// a type to represent the raw bytes of an encrypted note plaintext. -#[derive(Clone, Debug)] -pub struct NoteCiphertextBytes(pub [u8; ENC_CIPHERTEXT_SIZE_V3]); - -/// a type to represent the raw bytes of a compact note. -#[derive(Clone, Debug)] -pub struct CompactNotePlaintextBytes(pub [u8; COMPACT_NOTE_SIZE_V3]); - -/// a type to represent the raw bytes of an encrypted compact note. -#[derive(Clone, Debug)] -pub struct CompactNoteCiphertextBytes(pub [u8; COMPACT_NOTE_SIZE_V3]); - -impl AsMut<[u8]> for NotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl From<&[u8]> for NotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - NotePlaintextBytes(s.try_into().unwrap()) - } -} - -impl AsRef<[u8]> for NoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl From<&[u8]> for NoteCiphertextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - NoteCiphertextBytes(s.try_into().unwrap()) - } -} - -impl AsMut<[u8]> for CompactNotePlaintextBytes { - fn as_mut(&mut self) -> &mut [u8] { - self.0.as_mut() - } -} - -impl From<&[u8]> for CompactNotePlaintextBytes { - fn from(s: &[u8]) -> Self - where - Self: Sized, - { - CompactNotePlaintextBytes(s.try_into().unwrap()) - } -} - -impl AsRef<[u8]> for CompactNoteCiphertextBytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. -/// -/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs -pub(crate) fn prf_ock_orchard( - ovk: &OutgoingViewingKey, - cv: &ValueCommitment, - cmx_bytes: &[u8; 32], - ephemeral_key: &EphemeralKeyBytes, -) -> OutgoingCipherKey { - OutgoingCipherKey( - Params::new() - .hash_length(32) - .personal(PRF_OCK_ORCHARD_PERSONALIZATION) - .to_state() - .update(ovk.as_ref()) - .update(&cv.to_bytes()) - .update(cmx_bytes) - .update(ephemeral_key.as_ref()) - .finalize() - .as_bytes() - .try_into() - .unwrap(), - ) -} - -/// note_version will return the version of the note plaintext. -pub fn note_version(plaintext: &[u8]) -> Option { - match plaintext[0] { - 0x02 => Some(0x02), - 0x03 => Some(0x03), - _ => None, - } -} - -/// Domain-specific requirements: -/// - If the note version is 3, the `plaintext` must contain a valid encoding of a ZSA asset type. -fn orchard_parse_note_plaintext_without_memo( - domain: &OrchardDomainV3, - plaintext: &CompactNotePlaintextBytes, - get_validated_pk_d: F, -) -> Option<(Note, Address)> -where - F: FnOnce(&Diversifier) -> Option, -{ - // The unwraps below are guaranteed to succeed by the assertion above - let diversifier = Diversifier::from_bytes(plaintext.0[1..12].try_into().unwrap()); - let value = NoteValue::from_bytes(plaintext.0[12..20].try_into().unwrap()); - let rseed = Option::from(RandomSeed::from_bytes( - plaintext.0[20..COMPACT_NOTE_SIZE_V2].try_into().unwrap(), - &domain.rho, - ))?; - let pk_d = get_validated_pk_d(&diversifier)?; - let recipient = Address::from_parts(diversifier, pk_d); - - let asset = match note_version(plaintext.0.as_ref())? { - 0x02 => AssetBase::native(), - 0x03 => { - let bytes = plaintext.0[COMPACT_NOTE_SIZE_V2..COMPACT_NOTE_SIZE_V3] - .try_into() - .unwrap(); - AssetBase::from_bytes(bytes).unwrap() - } - _ => panic!("invalid note version"), - }; - - let note = Option::from(Note::from_parts(recipient, value, asset, domain.rho, rseed))?; - Some((note, recipient)) -} - -/// Orchard-specific note encryption logic. -#[derive(Debug)] -pub struct OrchardDomainV3 { - rho: Nullifier, -} - -impl OrchardDomainV3 { - /// Constructs a domain that can be used to trial-decrypt this action's output note. - pub fn for_action(act: &Action) -> Self { - OrchardDomainV3 { - rho: *act.nullifier(), - } - } - - /// Constructs a domain from a nullifier. - pub fn for_nullifier(nullifier: Nullifier) -> Self { - OrchardDomainV3 { rho: nullifier } - } -} - -impl Domain for OrchardDomainV3 { - type EphemeralSecretKey = EphemeralSecretKey; - type EphemeralPublicKey = EphemeralPublicKey; - type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey; - type SharedSecret = SharedSecret; - type SymmetricKey = Hash; - type Note = Note; - type Recipient = Address; - type DiversifiedTransmissionKey = DiversifiedTransmissionKey; - type IncomingViewingKey = PreparedIncomingViewingKey; - type OutgoingViewingKey = OutgoingViewingKey; - type ValueCommitment = ValueCommitment; - type ExtractedCommitment = ExtractedNoteCommitment; - type ExtractedCommitmentBytes = [u8; 32]; - type Memo = [u8; MEMO_SIZE]; - - type NotePlaintextBytes = NotePlaintextBytes; - type NoteCiphertextBytes = NoteCiphertextBytes; - type CompactNotePlaintextBytes = CompactNotePlaintextBytes; - type CompactNoteCiphertextBytes = CompactNoteCiphertextBytes; - - fn derive_esk(note: &Self::Note) -> Option { - Some(note.esk()) - } - - fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey { - *note.recipient().pk_d() - } - - fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { - PreparedEphemeralPublicKey::new(epk) - } - - fn ka_derive_public( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> Self::EphemeralPublicKey { - esk.derive_public(note.recipient().g_d()) - } - - fn ka_agree_enc( - esk: &Self::EphemeralSecretKey, - pk_d: &Self::DiversifiedTransmissionKey, - ) -> Self::SharedSecret { - esk.agree(pk_d) - } - - fn ka_agree_dec( - ivk: &Self::IncomingViewingKey, - epk: &Self::PreparedEphemeralPublicKey, - ) -> Self::SharedSecret { - epk.agree(ivk) - } - - fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey { - secret.kdf_orchard(ephemeral_key) - } - - fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> NotePlaintextBytes { - let mut np = [0u8; NOTE_PLAINTEXT_SIZE_V3]; - np[0] = 0x03; - np[1..12].copy_from_slice(note.recipient().diversifier().as_array()); - np[12..20].copy_from_slice(¬e.value().to_bytes()); - np[20..52].copy_from_slice(note.rseed().as_bytes()); - np[52..84].copy_from_slice(¬e.asset().to_bytes()); - np[84..].copy_from_slice(memo); - NotePlaintextBytes(np) - } - - fn derive_ock( - ovk: &Self::OutgoingViewingKey, - cv: &Self::ValueCommitment, - cmstar_bytes: &Self::ExtractedCommitmentBytes, - ephemeral_key: &EphemeralKeyBytes, - ) -> OutgoingCipherKey { - prf_ock_orchard(ovk, cv, cmstar_bytes, ephemeral_key) - } - - fn outgoing_plaintext_bytes( - note: &Self::Note, - esk: &Self::EphemeralSecretKey, - ) -> OutPlaintextBytes { - let mut op = [0; OUT_PLAINTEXT_SIZE]; - op[..32].copy_from_slice(¬e.recipient().pk_d().to_bytes()); - op[32..].copy_from_slice(&esk.0.to_repr()); - OutPlaintextBytes(op) - } - - fn epk_bytes(epk: &Self::EphemeralPublicKey) -> EphemeralKeyBytes { - epk.to_bytes() - } - - fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option { - EphemeralPublicKey::from_bytes(&ephemeral_key.0).into() - } - - fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment { - note.commitment().into() - } - - fn parse_note_plaintext_without_memo_ivk( - &self, - ivk: &Self::IncomingViewingKey, - plaintext: &CompactNotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - Some(DiversifiedTransmissionKey::derive(ivk, diversifier)) - }) - } - - fn parse_note_plaintext_without_memo_ovk( - &self, - pk_d: &Self::DiversifiedTransmissionKey, - plaintext: &CompactNotePlaintextBytes, - ) -> Option<(Self::Note, Self::Recipient)> { - orchard_parse_note_plaintext_without_memo(self, plaintext, |_| Some(*pk_d)) - } - - fn extract_memo( - &self, - plaintext: &NotePlaintextBytes, - ) -> (Self::CompactNotePlaintextBytes, Self::Memo) { - let (compact, memo) = plaintext.0.split_at(COMPACT_NOTE_SIZE_V3); - ( - CompactNotePlaintextBytes(compact.try_into().unwrap()), - memo.try_into().unwrap(), - ) - } - - fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option { - DiversifiedTransmissionKey::from_bytes(out_plaintext.0[0..32].try_into().unwrap()).into() - } - - fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option { - EphemeralSecretKey::from_bytes(out_plaintext.0[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()) - .into() - } -} - -impl BatchDomain for OrchardDomainV3 { - fn batch_kdf<'a>( - items: impl Iterator, &'a EphemeralKeyBytes)>, - ) -> Vec> { - let (shared_secrets, ephemeral_keys): (Vec<_>, Vec<_>) = items.unzip(); - - SharedSecret::batch_to_affine(shared_secrets) - .zip(ephemeral_keys.into_iter()) - .map(|(secret, ephemeral_key)| { - secret.map(|dhsecret| SharedSecret::kdf_orchard_inner(dhsecret, ephemeral_key)) - }) - .collect() - } -} - -/// Implementation of in-band secret distribution for Orchard bundles. -pub type OrchardNoteEncryption = zcash_note_encryption_zsa::NoteEncryption; - -impl ShieldedOutput for Action { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.encrypted_note().epk_bytes) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx().to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - Some(NoteCiphertextBytes(self.encrypted_note().enc_ciphertext)) - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - CompactNoteCiphertextBytes( - self.encrypted_note().enc_ciphertext[..COMPACT_NOTE_SIZE_V3] - .try_into() - .unwrap(), - ) - } -} - -/// A compact Action for light clients. -pub struct CompactAction { - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, -} - -impl fmt::Debug for CompactAction { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "CompactAction") - } -} - -impl From<&Action> for CompactAction -where - Action: ShieldedOutput, -{ - fn from(action: &Action) -> Self { - CompactAction { - nullifier: *action.nullifier(), - cmx: *action.cmx(), - ephemeral_key: action.ephemeral_key(), - enc_ciphertext: CompactNoteCiphertextBytes( - action.encrypted_note().enc_ciphertext[..COMPACT_NOTE_SIZE_V3] - .try_into() - .unwrap(), - ), - } - } -} - -impl ShieldedOutput for CompactAction { - fn ephemeral_key(&self) -> EphemeralKeyBytes { - EphemeralKeyBytes(self.ephemeral_key.0) - } - - fn cmstar_bytes(&self) -> [u8; 32] { - self.cmx.to_bytes() - } - - fn enc_ciphertext(&self) -> Option { - None - } - - fn enc_ciphertext_compact(&self) -> CompactNoteCiphertextBytes { - self.enc_ciphertext.clone() - } -} - -impl CompactAction { - /// Create a CompactAction from its constituent parts - pub fn from_parts( - nullifier: Nullifier, - cmx: ExtractedNoteCommitment, - ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: CompactNoteCiphertextBytes, - ) -> Self { - Self { - nullifier, - cmx, - ephemeral_key, - enc_ciphertext, - } - } - - ///Returns the nullifier of the note being spent. - pub fn nullifier(&self) -> Nullifier { - self.nullifier - } -} - -#[cfg(test)] -mod tests { - use proptest::prelude::*; - use rand::rngs::OsRng; - use zcash_note_encryption_zsa::{ - try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, Domain, - EphemeralKeyBytes, - }; - - use super::{ - note_version, orchard_parse_note_plaintext_without_memo, prf_ock_orchard, CompactAction, - OrchardDomainV3, OrchardNoteEncryption, - }; - use crate::{ - action::Action, - keys::{ - DiversifiedTransmissionKey, Diversifier, EphemeralSecretKey, IncomingViewingKey, - OutgoingViewingKey, PreparedIncomingViewingKey, - }, - note::{ - testing::arb_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, - TransmittedNoteCiphertext, - }, - primitives::redpallas, - value::{NoteValue, ValueCommitment}, - Address, Note, - }; - - proptest! { - #[test] - fn test_encoding_roundtrip( - note in arb_note(NoteValue::from_raw(100)), - ) { - let memo = &crate::test_vectors::note_encryption::test_vectors()[0].memo; - - // Encode. - let mut plaintext = OrchardDomainV3::note_plaintext_bytes(¬e, memo); - - // Decode. - let domain = OrchardDomainV3 { rho: note.rho() }; - let parsed_version = note_version(plaintext.as_mut()).unwrap(); - let (compact,parsed_memo) = domain.extract_memo(&plaintext); - - let (parsed_note, parsed_recipient) = orchard_parse_note_plaintext_without_memo(&domain, &compact, - |diversifier| { - assert_eq!(diversifier, ¬e.recipient().diversifier()); - Some(*note.recipient().pk_d()) - } - ).expect("Plaintext parsing failed"); - - // Check. - assert_eq!(parsed_note, note); - assert_eq!(parsed_recipient, note.recipient()); - assert_eq!(&parsed_memo, memo); - assert_eq!(parsed_version, 0x03); - } - } - - #[test] - fn test_vectors() { - let test_vectors = crate::test_vectors::note_encryption_v3::test_vectors(); - - for tv in test_vectors { - // - // Load the test vector components - // - - // Recipient key material - let ivk = PreparedIncomingViewingKey::new( - &IncomingViewingKey::from_bytes(&tv.incoming_viewing_key).unwrap(), - ); - let ovk = OutgoingViewingKey::from(tv.ovk); - let d = Diversifier::from_bytes(tv.default_d); - let pk_d = DiversifiedTransmissionKey::from_bytes(&tv.default_pk_d).unwrap(); - - // Received Action - let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); - let rho = Nullifier::from_bytes(&tv.rho).unwrap(); - let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); - - let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); - let ephemeral_key = EphemeralKeyBytes(tv.ephemeral_key); - - // Details about the expected note - let value = NoteValue::from_raw(tv.v); - let rseed = RandomSeed::from_bytes(tv.rseed, &rho).unwrap(); - - // - // Test the individual components - // - - let shared_secret = esk.agree(&pk_d); - assert_eq!(shared_secret.to_bytes(), tv.shared_secret); - - let k_enc = shared_secret.kdf_orchard(&ephemeral_key); - assert_eq!(k_enc.as_bytes(), tv.k_enc); - - let ock = prf_ock_orchard(&ovk, &cv_net, &cmx.to_bytes(), &ephemeral_key); - assert_eq!(ock.as_ref(), tv.ock); - - let recipient = Address::from_parts(d, pk_d); - - let asset = AssetBase::from_bytes(&tv.asset).unwrap(); - - let note = Note::from_parts(recipient, value, asset, rho, rseed).unwrap(); - assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); - - let action = Action::from_parts( - // rho is the nullifier in the receiving Action. - rho, - // We don't need a valid rk for this test. - redpallas::VerificationKey::dummy(), - cmx, - TransmittedNoteCiphertext { - epk_bytes: ephemeral_key.0, - enc_ciphertext: tv.c_enc, - out_ciphertext: tv.c_out, - }, - cv_net.clone(), - (), - ); - - // - // Test decryption - // (Tested first because it only requires immutable references.) - // - - let domain = OrchardDomainV3 { rho }; - - match try_note_decryption(&domain, &ivk, &action) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Note decryption failed"), - } - - match try_compact_note_decryption(&domain, &ivk, &CompactAction::from(&action)) { - Some((decrypted_note, decrypted_to)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - } - None => panic!("Compact note decryption failed"), - } - - match try_output_recovery_with_ovk(&domain, &ovk, &action, &cv_net, &tv.c_out) { - Some((decrypted_note, decrypted_to, decrypted_memo)) => { - assert_eq!(decrypted_note, note); - assert_eq!(decrypted_to, recipient); - assert_eq!(&decrypted_memo[..], &tv.memo[..]); - } - None => panic!("Output recovery failed"), - } - - // - // Test encryption - // - - let ne = OrchardNoteEncryption::new_with_esk(esk, Some(ovk), note, tv.memo); - - assert_eq!(ne.encrypt_note_plaintext().as_ref(), &tv.c_enc[..]); - assert_eq!( - &ne.encrypt_outgoing_plaintext(&cv_net, &cmx, &mut OsRng)[..], - &tv.c_out[..] - ); - } - } -} diff --git a/src/orchard_flavors.rs b/src/orchard_flavors.rs new file mode 100644 index 000000000..80c0c30de --- /dev/null +++ b/src/orchard_flavors.rs @@ -0,0 +1,9 @@ +//! Defines types and traits for the variations ("flavors") of the Orchard protocol (Vanilla and ZSA). + +/// Represents the standard ("Vanilla") variation ("flavor") of the Orchard protocol. +#[derive(Debug, Clone, Default)] +pub struct OrchardVanilla; + +/// Represents a ZSA variation ("flavor") of the Orchard protocol. +#[derive(Debug, Clone, Default)] +pub struct OrchardZSA; diff --git a/src/test_vectors.rs b/src/test_vectors.rs index 3cea120cd..b97f11b04 100644 --- a/src/test_vectors.rs +++ b/src/test_vectors.rs @@ -3,5 +3,5 @@ pub(crate) mod commitment_tree; pub(crate) mod issuance_auth_sig; pub(crate) mod keys; pub(crate) mod merkle_path; -pub(crate) mod note_encryption; -pub(crate) mod note_encryption_v3; +pub(crate) mod note_encryption_vanilla; +pub(crate) mod note_encryption_zsa; diff --git a/src/test_vectors/asset_base.rs b/src/test_vectors/asset_base.rs index 041d80471..4cfaa0207 100644 --- a/src/test_vectors/asset_base.rs +++ b/src/test_vectors/asset_base.rs @@ -1,4 +1,4 @@ -// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_asset_base) +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_zsa_asset_base) pub(crate) struct TestVector { pub(crate) key: [u8; 32], @@ -10,1022 +10,1022 @@ pub(crate) fn test_vectors() -> Vec { vec![ TestVector { key: [ - 0x16, 0x88, 0x4f, 0x1d, 0xbc, 0x92, 0x90, 0x89, 0xa4, 0x17, 0x6e, 0x84, 0x0b, 0xb5, - 0x81, 0xc8, 0x0e, 0x16, 0xe9, 0xb1, 0xab, 0xd6, 0x54, 0xe6, 0x2c, 0x8b, 0x0b, 0x95, - 0x70, 0x20, 0xb7, 0x48, + 0x4b, 0xec, 0xe1, 0xff, 0x00, 0xe2, 0xed, 0x77, 0x64, 0xae, 0x6b, 0xe2, 0x0d, 0x2f, + 0x67, 0x22, 0x04, 0xfc, 0x86, 0xcc, 0xed, 0xd6, 0xfc, 0x1f, 0x71, 0xdf, 0x02, 0xc7, + 0x51, 0x6d, 0x9f, 0x31, ], description: [ - 0x31, 0xc3, 0x85, 0xc7, 0xa7, 0x38, 0xc6, 0x88, 0x66, 0xc7, 0x8d, 0xc6, 0xa9, 0xc7, - 0xae, 0xc5, 0xb7, 0xc3, 0xaf, 0xc6, 0x85, 0xc7, 0x9f, 0xc6, 0x88, 0xe2, 0xb1, 0xbc, - 0xe1, 0x9a, 0xb7, 0xc3, 0x8c, 0xc7, 0x86, 0xc5, 0xbd, 0xc9, 0x80, 0xc3, 0x84, 0x4e, - 0x76, 0xc6, 0xa9, 0xc3, 0x91, 0x32, 0xc4, 0xab, 0xc9, 0x81, 0xce, 0x8c, 0xc4, 0xa7, - 0xc5, 0x97, 0xc6, 0xa6, 0xc4, 0xb3, 0xc5, 0xaa, 0xc7, 0x95, 0xc8, 0x99, 0xe1, 0x9b, - 0x91, 0xe1, 0x9a, 0xb5, 0xc7, 0x83, 0xc2, 0xa9, 0xc7, 0x80, 0xc7, 0xa5, 0xc8, 0x9d, - 0xce, 0x88, 0xc9, 0x8a, 0xc5, 0x92, 0xc7, 0x91, 0x5d, 0xc3, 0x93, 0x43, 0xc4, 0xa9, - 0xc8, 0x82, 0x25, 0xc3, 0xbb, 0xc3, 0x9d, 0xc8, 0xaf, 0xe1, 0x9a, 0xa5, 0xc8, 0xa0, - 0xc8, 0x8c, 0xc6, 0x86, 0xc3, 0xa4, 0xe1, 0x9b, 0x80, 0xc4, 0xbc, 0xc6, 0xb3, 0xc5, - 0x81, 0xe1, 0x9b, 0x97, 0xc5, 0x8e, 0xc8, 0x9d, 0xc6, 0xa1, 0xce, 0x8a, 0xc7, 0x88, - 0xc6, 0xb5, 0xc8, 0x83, 0xc3, 0xa2, 0xc4, 0x92, 0xc5, 0xb0, 0xc3, 0x8f, 0xc7, 0xab, - 0xe1, 0x9a, 0xac, 0xc2, 0xaf, 0xc7, 0x8a, 0x37, 0x72, 0x74, 0xe2, 0xb1, 0xbb, 0xc5, - 0x83, 0xe1, 0x9a, 0xa9, 0xe2, 0xb1, 0xa5, 0x5c, 0xc3, 0xbd, 0xc3, 0x88, 0xc9, 0x86, - 0x77, 0xc2, 0xb1, 0xc5, 0x9d, 0xc7, 0xa4, 0xc8, 0x9b, 0xcd, 0xb4, 0xc7, 0x81, 0xc4, - 0x9c, 0xc2, 0xbe, 0xe2, 0xb1, 0xa9, 0xc8, 0x82, 0xc6, 0xa0, 0xe1, 0x9a, 0xb5, 0xc2, - 0xa8, 0xc5, 0x82, 0xc3, 0x9a, 0xc6, 0xac, 0xc4, 0xa0, 0xc4, 0xb5, 0xc8, 0x93, 0xe2, - 0xb1, 0xa0, 0xc3, 0xbe, 0xc9, 0x84, 0x77, 0xc4, 0xb4, 0xc8, 0xbe, 0x5c, 0xc6, 0xbe, - 0xc3, 0xb0, 0xc9, 0x84, 0x28, 0xc6, 0x8d, 0xc3, 0xb2, 0xc8, 0xaa, 0xc8, 0x8f, 0xc8, - 0xb2, 0xc7, 0x8b, 0xcd, 0xba, 0xc3, 0xb5, 0xc9, 0x8e, 0xc4, 0xb4, 0xe2, 0xb1, 0xaa, - 0xe1, 0x9a, 0xab, 0xc2, 0xbd, 0xc8, 0xac, 0xc7, 0x8e, 0xc8, 0x95, 0xc2, 0xa9, 0xe1, - 0x9a, 0xa2, 0xe1, 0x9b, 0x8b, 0xc7, 0x98, 0xc6, 0x94, 0xe1, 0x9b, 0x9f, 0xc5, 0x98, - 0xc4, 0xbd, 0x39, 0x40, 0xc8, 0xa9, 0xc5, 0x94, 0x53, 0xe1, 0x9b, 0xac, 0xc8, 0xa0, - 0xc5, 0xb3, 0x76, 0x74, 0xc8, 0xa3, 0xc3, 0x81, 0x68, 0xcd, 0xbc, 0xc7, 0xba, 0x6e, - 0x73, 0xc5, 0x8f, 0xce, 0x8c, 0xc7, 0x9d, 0xc5, 0xaf, 0x45, 0xc3, 0xb0, 0xc3, 0x86, - 0xc4, 0x91, 0xc8, 0x94, 0xc5, 0x93, 0xc4, 0x81, 0xe2, 0xb1, 0xa1, 0xc7, 0x92, 0xc4, - 0xb0, 0xc8, 0x87, 0x4e, 0x51, 0xc6, 0xab, 0xc4, 0xb2, 0xe1, 0x9b, 0x8d, 0xc4, 0x8a, - 0xc4, 0xa9, 0xc3, 0xb3, 0xe1, 0x9b, 0x8f, 0xce, 0x8a, 0x3b, 0xc7, 0xa9, 0xc5, 0x9c, - 0x45, 0xe2, 0xb1, 0xab, 0xc6, 0xbc, 0xc2, 0xb5, 0xe1, 0x9b, 0x8d, 0xc4, 0x84, 0xe2, - 0xb1, 0xa8, 0xc7, 0xa9, 0xc6, 0x94, 0xc6, 0xa6, 0xc6, 0xba, 0xe1, 0x9b, 0xab, 0xc2, - 0xac, 0xc4, 0x8e, 0xe2, 0xb1, 0xb9, 0xc2, 0xb0, 0xce, 0x88, 0xc5, 0xb7, 0x70, 0xe2, - 0xb1, 0xac, 0xce, 0x86, 0x54, 0xc9, 0x8d, 0xc4, 0xa6, 0xcd, 0xb7, 0xc4, 0xaf, 0x75, - 0x3b, 0x3f, 0xc6, 0x81, 0xc8, 0xab, 0xc8, 0xa4, 0x78, 0xc8, 0xab, 0xc4, 0xbb, 0x2e, - 0xc8, 0x89, 0xc3, 0xb0, 0xc5, 0x90, 0x74, 0xc2, 0xa8, 0xc4, 0xa4, 0xc5, 0xbf, 0xe1, - 0x9a, 0xb2, 0xc2, 0xbc, 0x4b, 0xc6, 0xbf, 0xc7, 0xbc, 0xc7, 0xa5, 0xc3, 0xba, 0xc3, - 0x86, 0xc4, 0xb4, 0xc8, 0xa4, 0xc3, 0x9c, 0xe2, 0xb1, 0xb0, 0xc5, 0x92, 0xc4, 0x97, - 0xe1, 0x9b, 0xaa, 0xc6, 0xbf, 0xc5, 0x9e, 0xc7, 0x84, 0xc4, 0xaf, 0xc8, 0xaa, 0xc8, - 0x89, 0xc4, 0xb3, 0xc2, 0xbb, 0xc4, 0x97, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x41, 0xb7, 0x75, 0x0a, 0x66, 0x59, 0xb2, 0xc8, 0x0f, 0x56, 0x7d, 0x67, 0x78, 0xd1, - 0x2d, 0x81, 0xc4, 0x7a, 0x46, 0xef, 0xdf, 0xfb, 0x63, 0x80, 0x6c, 0x01, 0x7c, 0xa5, - 0x61, 0x50, 0xa7, 0xa6, + 0xa7, 0x62, 0x5a, 0x60, 0xff, 0x6a, 0xdc, 0x49, 0x8a, 0xc2, 0xf5, 0x7b, 0x4d, 0xbe, + 0xd3, 0x67, 0x26, 0xba, 0xe6, 0x40, 0x1f, 0x3e, 0x01, 0xc2, 0x61, 0x17, 0x0b, 0x02, + 0x81, 0x0f, 0x08, 0x19, ], }, TestVector { key: [ - 0x72, 0x73, 0xb6, 0x57, 0xd9, 0x71, 0xa4, 0x5e, 0x72, 0x24, 0x0c, 0x7a, 0xaa, 0xa7, - 0xd0, 0x68, 0x5d, 0x06, 0xd7, 0x99, 0x9b, 0x0a, 0x19, 0xc4, 0xce, 0xa3, 0x27, 0x88, - 0xa6, 0xab, 0x51, 0x3d, + 0x16, 0x88, 0x4f, 0x1d, 0xbc, 0x92, 0x90, 0x89, 0xa4, 0x17, 0x6e, 0x84, 0x0b, 0xb5, + 0x81, 0xc8, 0x0e, 0x16, 0xe9, 0xb1, 0xab, 0xd6, 0x54, 0xe6, 0x2c, 0x8b, 0x0b, 0x95, + 0x70, 0x20, 0xb7, 0x48, ], description: [ - 0xc5, 0x96, 0xc6, 0xac, 0xc4, 0xb3, 0x28, 0x5f, 0xc2, 0xa5, 0x66, 0xe1, 0x9b, 0x8c, - 0xc8, 0xa5, 0xc3, 0x95, 0xc4, 0xaa, 0xc3, 0xa9, 0xc2, 0xbd, 0xe2, 0xb1, 0xbb, 0x52, - 0x48, 0xe1, 0x9b, 0x9f, 0xe2, 0xb1, 0xaf, 0xe1, 0x9b, 0x87, 0xc7, 0x9f, 0x2b, 0xe1, - 0x9a, 0xab, 0xc6, 0x97, 0x6b, 0xc8, 0x8e, 0xe1, 0x9a, 0xbb, 0xc6, 0xa0, 0xc7, 0x89, - 0xc2, 0xbd, 0xc8, 0xad, 0xc4, 0xbe, 0xe1, 0x9b, 0x82, 0xc8, 0x9c, 0xe1, 0x9a, 0xac, - 0x3f, 0xe1, 0x9a, 0xa2, 0xc9, 0x8c, 0xe1, 0x9a, 0xa2, 0xe1, 0x9b, 0x86, 0xc5, 0x9d, - 0xc4, 0xb4, 0xc7, 0x80, 0xc3, 0xb5, 0x71, 0x6b, 0x6c, 0xc8, 0x96, 0xc7, 0xbb, 0xc8, - 0x9a, 0xc7, 0x90, 0xc2, 0xb1, 0xc4, 0x89, 0xc3, 0x99, 0xc3, 0xbd, 0xc7, 0xae, 0xc2, - 0xa3, 0xc4, 0xa7, 0x58, 0xc4, 0x80, 0xc4, 0x9f, 0xc7, 0xbb, 0xc4, 0x8b, 0xc6, 0x8d, - 0xc5, 0x98, 0xc2, 0xb4, 0xc5, 0x93, 0xc4, 0x9c, 0xc8, 0x83, 0xc7, 0xbc, 0x30, 0xc4, - 0x87, 0xc3, 0x85, 0xe1, 0x9a, 0xb2, 0xc4, 0xa8, 0xc7, 0xb8, 0xc6, 0xab, 0x36, 0xc4, - 0xa6, 0x3b, 0xc7, 0xa3, 0xc7, 0x82, 0xcd, 0xb2, 0xc7, 0x80, 0xc3, 0xb0, 0xc2, 0xbd, - 0xe2, 0xb1, 0xb4, 0xc6, 0x93, 0xc4, 0x8f, 0x5d, 0xc6, 0x8f, 0x35, 0xc9, 0x85, 0xe1, - 0x9b, 0x95, 0xc7, 0xa4, 0xe2, 0xb1, 0xbf, 0xc4, 0x85, 0xc5, 0xa4, 0xc7, 0x83, 0xc8, - 0x81, 0xc8, 0x89, 0xc8, 0x98, 0xc5, 0xac, 0xe2, 0xb1, 0xb4, 0xc8, 0x8a, 0xc7, 0xa1, - 0xc7, 0x9d, 0xc6, 0xae, 0xc6, 0x9c, 0xc6, 0x98, 0xc3, 0xa3, 0xc4, 0x9b, 0x43, 0xe1, - 0x9b, 0x82, 0xc2, 0xa3, 0xc3, 0xa6, 0xc5, 0x85, 0xc8, 0x89, 0xe1, 0x9b, 0x99, 0x5f, - 0xc4, 0xa9, 0xe1, 0x9b, 0x95, 0xc2, 0xb7, 0xc3, 0x89, 0xe2, 0xb1, 0xb7, 0xc4, 0x96, - 0xe2, 0xb1, 0xa2, 0x5b, 0xc3, 0xac, 0x58, 0xc6, 0xa9, 0xcd, 0xbd, 0xc8, 0xa1, 0xe2, - 0xb1, 0xa6, 0xc8, 0x8e, 0xc5, 0xb3, 0xc7, 0x93, 0xc6, 0xa1, 0xc9, 0x8d, 0xc5, 0xa2, - 0x29, 0xce, 0x8c, 0xe1, 0x9b, 0x99, 0xc5, 0xaf, 0xc8, 0xbd, 0x2b, 0xc7, 0xb2, 0xc7, - 0xba, 0xcd, 0xb7, 0xc2, 0xbc, 0xe1, 0x9b, 0xa4, 0xc7, 0xb9, 0xc3, 0x8d, 0xe2, 0xb1, - 0xa0, 0xc2, 0xa4, 0xc8, 0x98, 0xc8, 0xa4, 0x49, 0xc5, 0xbb, 0xc3, 0x96, 0xc3, 0x94, - 0x5c, 0xc5, 0x8e, 0xe2, 0xb1, 0xae, 0x73, 0x74, 0xc6, 0xab, 0xc7, 0x86, 0xe2, 0xb1, - 0xb3, 0xe1, 0x9b, 0x8f, 0xc8, 0xb3, 0xc4, 0xa6, 0xc6, 0xb2, 0xc8, 0xa4, 0xc7, 0x86, - 0xc7, 0xa8, 0xe1, 0x9b, 0xa2, 0xc8, 0xaa, 0xc4, 0x92, 0xcd, 0xb2, 0x7e, 0xc9, 0x8d, - 0x4e, 0x45, 0xc6, 0x8a, 0xc6, 0xae, 0xc4, 0x8d, 0x3f, 0x59, 0xc5, 0x96, 0xe2, 0xb1, - 0xbd, 0xe1, 0x9a, 0xbc, 0xc3, 0x90, 0xc8, 0x8e, 0xc3, 0xa1, 0xc7, 0xad, 0xe1, 0x9a, - 0xb9, 0xc5, 0x8e, 0xe1, 0x9b, 0xa5, 0xe1, 0x9a, 0xb5, 0xc6, 0x83, 0xc7, 0x8e, 0xc8, - 0xbf, 0xc2, 0xbc, 0xc5, 0x8a, 0xc4, 0x8e, 0xcd, 0xb5, 0xc5, 0x97, 0xc4, 0x8f, 0xc2, - 0xaa, 0xc7, 0xaf, 0xc3, 0xbb, 0xc2, 0xa2, 0x49, 0x32, 0xc7, 0xa9, 0xc2, 0xb4, 0xe1, - 0x9b, 0xa1, 0xe1, 0x9b, 0xa3, 0xe2, 0xb1, 0xbf, 0x53, 0xc8, 0x96, 0xc8, 0xac, 0xc8, - 0xa3, 0xc3, 0xa0, 0x29, 0xe1, 0x9b, 0x89, 0xc5, 0xb3, 0xc6, 0xb2, 0xc3, 0xab, 0xc3, - 0x99, 0xc4, 0x9d, 0xc6, 0xb6, 0xc4, 0xa3, 0x3b, 0xe1, 0x9b, 0xa6, 0xc6, 0x95, 0xe1, - 0x9b, 0xa5, 0xc2, 0xbc, 0xc7, 0xa0, 0xc7, 0xa3, 0xe1, 0x9b, 0x8a, 0xc4, 0xb4, 0xc9, - 0x80, 0x64, 0x6f, 0xc6, 0x9e, 0xc4, 0x94, 0xc8, 0x8c, 0xc6, 0xa4, 0xc7, 0x96, 0xc3, - 0x9b, 0xc7, 0x91, 0xc5, 0x9d, 0x6d, 0xc7, 0x8a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0xea, 0x1b, 0xf2, 0x91, 0x8d, 0xe2, 0xfb, 0x1a, 0x26, 0x10, 0x83, 0x25, 0x20, 0xbd, - 0x80, 0x71, 0x98, 0xe1, 0x26, 0x73, 0xab, 0xd2, 0x3b, 0x24, 0x1d, 0xba, 0x06, 0x08, - 0x20, 0x2e, 0x56, 0x17, + 0xb9, 0xfc, 0x57, 0x2c, 0x5a, 0xd8, 0x81, 0x26, 0xff, 0x6b, 0x7c, 0x65, 0x9f, 0x78, + 0x74, 0x91, 0xf7, 0xc2, 0x7c, 0x5f, 0xe4, 0xff, 0x24, 0x20, 0x8a, 0x02, 0xbc, 0x94, + 0x5e, 0x55, 0x4a, 0xbf, ], }, TestVector { key: [ - 0xec, 0x05, 0xbb, 0x7f, 0x06, 0x5e, 0x25, 0x6f, 0xf4, 0x54, 0xf8, 0xa8, 0xdf, 0x6f, - 0x2f, 0x9b, 0x8a, 0x8c, 0x95, 0x08, 0xca, 0xac, 0xfe, 0xe9, 0x52, 0x1c, 0xbe, 0x68, - 0x9d, 0xd1, 0x12, 0x0f, + 0xd4, 0x22, 0x9e, 0x19, 0x5e, 0x25, 0xf6, 0x02, 0xa2, 0x18, 0x61, 0x22, 0xcb, 0x4e, + 0x78, 0x76, 0x7b, 0x3c, 0x66, 0xac, 0x39, 0x08, 0x08, 0xd2, 0xd1, 0xb4, 0x04, 0x42, + 0xda, 0x7f, 0x00, 0x66, ], description: [ - 0xc7, 0x82, 0x7e, 0xc5, 0xb9, 0xc6, 0xa6, 0xc5, 0xa3, 0xe1, 0x9a, 0xae, 0xe2, 0xb1, - 0xa6, 0xc8, 0xb2, 0xc8, 0xbc, 0xc5, 0x9f, 0xc8, 0x83, 0xc3, 0x82, 0xc4, 0x9a, 0xe2, - 0xb1, 0xa8, 0xc4, 0x88, 0xe2, 0xb1, 0xad, 0xc8, 0x98, 0xc3, 0xbb, 0xc3, 0x92, 0xc4, - 0xb7, 0xc7, 0xba, 0xc7, 0x8b, 0x72, 0xc6, 0x8d, 0x75, 0xc3, 0x82, 0xc3, 0xad, 0xc5, - 0x8a, 0xc7, 0xb1, 0xc8, 0xbb, 0xc5, 0x9e, 0xe2, 0xb1, 0xb3, 0xc3, 0xa5, 0xe1, 0x9a, - 0xb8, 0xc5, 0x81, 0x63, 0xc5, 0x98, 0xe1, 0x9b, 0x80, 0xc5, 0x86, 0xc4, 0xa9, 0xe1, - 0x9b, 0xa4, 0x60, 0xc8, 0xa0, 0xc3, 0x97, 0xc4, 0xa4, 0xc5, 0x81, 0xc3, 0x95, 0xc4, - 0xb0, 0xc2, 0xb2, 0xe1, 0x9b, 0x94, 0xc7, 0x8c, 0x46, 0x60, 0xe2, 0xb1, 0xa0, 0xc4, - 0xa5, 0xc7, 0xa9, 0xc6, 0xa8, 0xe1, 0x9b, 0x9c, 0xc5, 0xb4, 0xc5, 0x82, 0xc8, 0x80, - 0xc4, 0x9b, 0xe2, 0xb1, 0xa7, 0xc4, 0x95, 0xc5, 0x93, 0xc8, 0x83, 0xc4, 0x9f, 0xc4, - 0xa1, 0xc4, 0xb1, 0xc3, 0xb0, 0xc8, 0xa5, 0xc6, 0x87, 0xc5, 0xb5, 0xe1, 0x9b, 0xaf, - 0xc9, 0x8e, 0xc2, 0xac, 0xe2, 0xb1, 0xba, 0xc7, 0x9f, 0xc5, 0xa1, 0xc6, 0x94, 0xc3, - 0xa5, 0xc8, 0x8b, 0xc8, 0xa5, 0xe2, 0xb1, 0xb2, 0xc7, 0xab, 0xe1, 0x9b, 0x91, 0xc3, - 0xaa, 0xc6, 0xb5, 0xc4, 0xbf, 0xc5, 0x80, 0xc4, 0xb4, 0xc3, 0xb6, 0xe2, 0xb1, 0xb3, - 0x5e, 0xc8, 0xb6, 0xe1, 0x9a, 0xab, 0xe2, 0xb1, 0xa7, 0xc5, 0x98, 0xe1, 0x9b, 0x9e, - 0x69, 0xe1, 0x9b, 0x8b, 0xe2, 0xb1, 0xbf, 0xc9, 0x88, 0xe2, 0xb1, 0xb5, 0x71, 0x29, - 0xc6, 0x85, 0xc7, 0xab, 0xe1, 0x9b, 0xa9, 0x6d, 0xc7, 0x90, 0xc3, 0x9b, 0xc5, 0x8e, - 0xe1, 0x9b, 0x87, 0xc7, 0x9f, 0xc5, 0xa3, 0xc5, 0xb9, 0x74, 0x7d, 0xc4, 0x94, 0xe1, - 0x9b, 0x93, 0xc7, 0xbc, 0xc8, 0x96, 0x58, 0xe2, 0xb1, 0xaf, 0xe2, 0xb1, 0xbc, 0xc2, - 0xb4, 0xc8, 0xbd, 0xc8, 0xab, 0xc4, 0xb7, 0x42, 0xc2, 0xb5, 0xc7, 0xa5, 0xc5, 0x9c, - 0xcd, 0xb3, 0xc9, 0x87, 0xe2, 0xb1, 0xa0, 0xc4, 0xa3, 0xc8, 0xad, 0xc5, 0x90, 0xe1, - 0x9b, 0x81, 0xc5, 0x9a, 0xc4, 0xa8, 0xc4, 0xb0, 0x7e, 0xc5, 0xaf, 0x60, 0xe1, 0x9a, - 0xa1, 0xc3, 0xb3, 0xc6, 0xb2, 0xe1, 0x9b, 0x97, 0x3c, 0xc4, 0xba, 0xc5, 0xbb, 0x42, - 0xc4, 0x97, 0x66, 0xc8, 0x91, 0xc3, 0xb2, 0xc2, 0xa5, 0xe1, 0x9b, 0xa0, 0xc4, 0xaf, - 0xc8, 0xbf, 0xc8, 0x8a, 0xc5, 0xa1, 0xc3, 0x98, 0xc8, 0x8c, 0xc5, 0x82, 0xe1, 0x9b, - 0x89, 0xc5, 0x88, 0xc8, 0xa4, 0xc6, 0xbc, 0x7a, 0xc4, 0xa1, 0xc3, 0xb0, 0xc5, 0x88, - 0x7b, 0xe1, 0x9b, 0xa1, 0xc2, 0xae, 0xc7, 0x88, 0xc5, 0x9b, 0x71, 0xc7, 0xa7, 0xe1, - 0x9a, 0xa2, 0xc3, 0x8d, 0x50, 0xc6, 0x82, 0xc2, 0xb8, 0xc8, 0xab, 0xc6, 0xa5, 0xc7, - 0x9a, 0xe1, 0x9b, 0x81, 0xc6, 0xb8, 0xc8, 0xb8, 0x32, 0xe1, 0x9b, 0x9a, 0xc5, 0x92, - 0x21, 0xc2, 0xb1, 0xe1, 0x9b, 0xa4, 0xe2, 0xb1, 0xab, 0xe2, 0xb1, 0xbc, 0xc3, 0x9d, - 0xc4, 0xad, 0xc5, 0x83, 0xc7, 0x91, 0x7d, 0xc3, 0xb3, 0xc8, 0x9f, 0xc4, 0x94, 0x40, - 0xc4, 0xb2, 0xc8, 0x92, 0xc7, 0xa1, 0xc3, 0xbc, 0xe1, 0x9b, 0x97, 0xc2, 0xbb, 0xc5, - 0x9a, 0xc2, 0xb2, 0x52, 0xe1, 0x9a, 0xa9, 0x4c, 0xc2, 0xa2, 0xe2, 0xb1, 0xa9, 0xe1, - 0x9b, 0x87, 0xc5, 0x9c, 0xc4, 0x81, 0xc7, 0xb9, 0xe2, 0xb1, 0xad, 0xc4, 0xb3, 0xc5, - 0xa1, 0x64, 0xc5, 0x96, 0xc3, 0x9c, 0xc8, 0xaf, 0xc8, 0x91, 0xe2, 0xb1, 0xaf, 0xc4, - 0x9d, 0xc8, 0x98, 0xe1, 0x9b, 0x8c, 0xc6, 0x89, 0xc6, 0xbc, 0xc8, 0xbc, 0xc5, 0x81, - 0x37, 0x45, 0xc7, 0xa3, 0xc5, 0x81, 0x5a, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x67, 0x0a, 0x16, 0x33, 0xc7, 0xae, 0x83, 0x6f, 0x14, 0x68, 0xb0, 0x6e, 0x24, 0xd0, - 0x8b, 0x84, 0x27, 0x6d, 0xf6, 0x78, 0xe7, 0xe5, 0x0d, 0x6c, 0x35, 0xce, 0x42, 0x98, - 0xbd, 0xc6, 0xb7, 0x92, + 0x69, 0x03, 0x15, 0xe6, 0xb7, 0xbb, 0xbc, 0x9d, 0x88, 0x60, 0x17, 0xfd, 0x0e, 0xb1, + 0x38, 0x67, 0x16, 0xc2, 0xb4, 0x3f, 0x43, 0xf9, 0xa1, 0xc5, 0x42, 0x74, 0xd7, 0x4a, + 0x50, 0xbc, 0x22, 0x8d, ], }, TestVector { key: [ - 0x81, 0x8f, 0x50, 0xce, 0x47, 0x10, 0xf4, 0xeb, 0x11, 0xe7, 0x43, 0xe6, 0x40, 0x85, - 0x44, 0xaa, 0x3c, 0x12, 0x3c, 0x7f, 0x07, 0xe2, 0xaa, 0xbb, 0x91, 0xaf, 0xc4, 0xec, - 0x48, 0x78, 0x8d, 0xe9, + 0x72, 0x73, 0xb6, 0x57, 0xd9, 0x71, 0xa4, 0x5e, 0x72, 0x24, 0x0c, 0x7a, 0xaa, 0xa7, + 0xd0, 0x68, 0x5d, 0x06, 0xd7, 0x99, 0x9b, 0x0a, 0x19, 0xc4, 0xce, 0xa3, 0x27, 0x88, + 0xa6, 0xab, 0x51, 0x3d, ], description: [ - 0xc7, 0x98, 0xe1, 0x9a, 0xb2, 0x2a, 0xc2, 0xa5, 0xc7, 0xb7, 0xc8, 0xaa, 0xc4, 0x9b, - 0xc9, 0x84, 0xc5, 0xaf, 0x4d, 0xc3, 0x83, 0xc2, 0xa3, 0xc7, 0xaf, 0xc6, 0x92, 0xe2, - 0xb1, 0xbe, 0xc8, 0xbf, 0xe1, 0x9b, 0x83, 0xc7, 0x80, 0xc2, 0xb8, 0xc7, 0xa1, 0x6c, - 0xe1, 0x9b, 0xad, 0xc3, 0xa3, 0xc7, 0x97, 0x3b, 0xc9, 0x87, 0xce, 0x8a, 0x51, 0xc8, - 0xbc, 0xc7, 0xa4, 0xc2, 0xa4, 0x2d, 0xc3, 0xb6, 0x34, 0xc9, 0x8e, 0xc5, 0x8f, 0xc9, - 0x86, 0xc5, 0xba, 0xe1, 0x9b, 0x99, 0x76, 0xc6, 0x85, 0xc3, 0xa2, 0xc8, 0x99, 0xc3, - 0x91, 0xc4, 0xb3, 0xc5, 0xa1, 0xe2, 0xb1, 0xbb, 0xc7, 0xa0, 0xc6, 0x9d, 0xc3, 0x89, - 0xc9, 0x86, 0xc5, 0xa1, 0xe1, 0x9b, 0x98, 0xc5, 0xb7, 0xc5, 0xae, 0xc5, 0xb6, 0xe1, - 0x9b, 0x89, 0xc8, 0x98, 0x36, 0xc6, 0x95, 0xc4, 0xb5, 0xc8, 0x85, 0x5c, 0xe1, 0x9b, - 0x99, 0xe2, 0xb1, 0xaf, 0xc3, 0x92, 0xc7, 0x89, 0xc7, 0xb5, 0xc5, 0xbd, 0xc3, 0xa5, - 0xc6, 0x93, 0xc4, 0xac, 0xe1, 0x9b, 0x87, 0xc6, 0x9e, 0xc5, 0xa5, 0xcd, 0xbd, 0xe1, - 0x9b, 0x8a, 0xc6, 0x9d, 0xc9, 0x85, 0xc4, 0xa4, 0x5c, 0x6f, 0xc6, 0x93, 0x76, 0xc3, - 0xa6, 0xe1, 0x9a, 0xad, 0xcd, 0xbb, 0xc2, 0xab, 0xc2, 0xa3, 0xe2, 0xb1, 0xaf, 0xc3, - 0xb6, 0xc6, 0x9f, 0xc5, 0xa5, 0x37, 0x34, 0xe1, 0x9b, 0x8c, 0xc5, 0x8e, 0xc2, 0xb9, - 0xe2, 0xb1, 0xba, 0xc9, 0x88, 0xc8, 0xba, 0xc7, 0x95, 0xc3, 0xa2, 0xc5, 0x9a, 0xc7, - 0x83, 0xc6, 0xb5, 0xc9, 0x8a, 0xc7, 0xb0, 0xe2, 0xb1, 0xab, 0xc5, 0xab, 0xc7, 0x8f, - 0xc3, 0xb6, 0xc4, 0xa7, 0xe1, 0x9a, 0xbb, 0x4e, 0xe1, 0x9b, 0x85, 0x5a, 0xc8, 0xb7, - 0x21, 0xe1, 0x9b, 0x87, 0x25, 0xc6, 0xb4, 0xc5, 0x92, 0xc8, 0xba, 0xc2, 0xa3, 0xc2, - 0xae, 0xe1, 0x9b, 0x89, 0xc5, 0xac, 0x67, 0xc5, 0x98, 0xc7, 0x81, 0xce, 0x85, 0xe2, - 0xb1, 0xb5, 0xc3, 0xab, 0xe2, 0xb1, 0xbd, 0xe1, 0x9b, 0x93, 0xc7, 0x91, 0xc8, 0xa0, - 0xc5, 0x83, 0xc6, 0xb4, 0xc8, 0xb6, 0xc8, 0x9a, 0xe1, 0x9b, 0x93, 0xc7, 0xa3, 0xe1, - 0x9a, 0xba, 0xc9, 0x81, 0xc5, 0xa5, 0xc5, 0xb8, 0x33, 0xc5, 0x86, 0xc8, 0x97, 0x77, - 0xe1, 0x9a, 0xb7, 0xc7, 0xa2, 0xc2, 0xaf, 0xce, 0x88, 0xe1, 0x9a, 0xaa, 0x33, 0xc8, - 0x8f, 0xc4, 0xb9, 0xc8, 0xaf, 0xc3, 0x81, 0xc6, 0xbc, 0xc9, 0x80, 0xc7, 0xb6, 0x46, - 0xe1, 0x9a, 0xa3, 0x6a, 0xc4, 0x85, 0xe1, 0x9b, 0x85, 0xc5, 0x8f, 0xcd, 0xbe, 0x71, - 0x2a, 0xc7, 0x95, 0xc6, 0xa1, 0xc3, 0xaf, 0xe1, 0x9b, 0xa6, 0xc3, 0x93, 0xc3, 0xa5, - 0xc7, 0x9a, 0xc5, 0xb4, 0x50, 0xc7, 0x81, 0xc3, 0x88, 0xc7, 0x87, 0x5a, 0xc3, 0x87, - 0xc3, 0xa2, 0xc7, 0x8a, 0xc8, 0xb9, 0xc3, 0xbb, 0xe1, 0x9a, 0xab, 0xc3, 0xaa, 0xc7, - 0x99, 0xc7, 0x89, 0xc3, 0x98, 0x7c, 0xc6, 0x83, 0x2d, 0xe2, 0xb1, 0xa2, 0xc6, 0x96, - 0xe1, 0x9a, 0xb1, 0x46, 0xe2, 0xb1, 0xa9, 0xc6, 0xaf, 0xc3, 0xbe, 0xe1, 0x9b, 0xa2, - 0xe1, 0x9b, 0x91, 0xc2, 0xb1, 0xc8, 0x8c, 0xc4, 0xb2, 0x75, 0xc4, 0xb9, 0xc5, 0xa6, - 0x52, 0xc6, 0xbc, 0xc3, 0xab, 0xe1, 0x9a, 0xb9, 0xc4, 0xb6, 0xc6, 0xb4, 0xc7, 0xa7, - 0xc2, 0xa1, 0x2d, 0x73, 0x6c, 0xc3, 0xae, 0xc3, 0x98, 0xc8, 0x91, 0xe2, 0xb1, 0xa1, - 0xc4, 0x9c, 0xc5, 0xa9, 0xe1, 0x9b, 0x80, 0xc2, 0xba, 0x71, 0xc3, 0x8c, 0x3b, 0xc3, - 0xb6, 0xc7, 0x95, 0xc5, 0xab, 0xc3, 0x81, 0xc6, 0xbf, 0xc5, 0x9a, 0xe1, 0x9b, 0xab, - 0xc8, 0x9c, 0xc8, 0x83, 0xc5, 0x9f, 0xc5, 0x9a, 0xc5, 0xae, 0xe1, 0x9a, 0xb4, 0xc5, - 0x9a, 0xc3, 0xa4, 0xc8, 0xab, 0xc7, 0xba, 0x69, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x7e, 0x26, 0x94, 0x4d, 0x61, 0xa6, 0xc8, 0xfe, 0x89, 0x5e, 0xda, 0x8d, 0x1c, 0x72, - 0x77, 0x13, 0xe9, 0x65, 0x61, 0xb7, 0x56, 0x56, 0xfd, 0xa2, 0x07, 0x0b, 0x2b, 0x0c, - 0x9d, 0xe1, 0x4d, 0x85, + 0x9b, 0xe1, 0x71, 0xf6, 0xf2, 0x77, 0x5f, 0x14, 0xa8, 0x8a, 0xf7, 0x42, 0x5f, 0xb8, + 0x37, 0xe8, 0x4e, 0x4f, 0xb0, 0x8b, 0x69, 0xdc, 0xa7, 0x0d, 0x96, 0x70, 0x94, 0xd6, + 0x78, 0xb9, 0x66, 0x92, ], }, TestVector { key: [ - 0xae, 0x36, 0xb6, 0x1a, 0x3d, 0x10, 0xf1, 0xaa, 0x75, 0x2a, 0xb1, 0xdc, 0x16, 0xe3, - 0xe4, 0x9b, 0x6a, 0xc0, 0xd2, 0xae, 0x19, 0x07, 0xd2, 0xe6, 0x94, 0x25, 0xec, 0x12, - 0xc9, 0x3a, 0xae, 0xbc, + 0xce, 0xb7, 0x5a, 0x43, 0x9f, 0xf0, 0x16, 0x15, 0x80, 0xbf, 0x29, 0x57, 0x24, 0xc6, + 0xd9, 0x2d, 0x31, 0xb7, 0xaa, 0x02, 0x84, 0x03, 0x39, 0x44, 0x49, 0x64, 0x48, 0x6f, + 0xae, 0xa8, 0x90, 0xe5, ], description: [ - 0xc4, 0x9e, 0xe2, 0xb1, 0xa6, 0xe1, 0x9a, 0xb3, 0x2e, 0xe2, 0xb1, 0xb4, 0xc7, 0x8a, - 0xe1, 0x9b, 0xaf, 0xc4, 0xa9, 0xe2, 0xb1, 0xb5, 0x26, 0xc5, 0x8a, 0xc3, 0xa9, 0x7b, - 0xc7, 0xac, 0xc8, 0xb4, 0xe1, 0x9a, 0xb1, 0xe1, 0x9b, 0x85, 0xc6, 0x88, 0xc6, 0x80, - 0xe1, 0x9a, 0xb0, 0xc5, 0xa2, 0x23, 0xc4, 0x9f, 0xc2, 0xa1, 0xe1, 0x9b, 0xac, 0xc6, - 0x98, 0x70, 0x5c, 0xc9, 0x89, 0xc4, 0xa0, 0xc4, 0xaa, 0xc5, 0xa3, 0x79, 0xc3, 0xab, - 0xce, 0x89, 0xc9, 0x83, 0xc3, 0xab, 0xe2, 0xb1, 0xa0, 0x3d, 0x6a, 0xc4, 0x93, 0xe1, - 0x9b, 0xab, 0x7a, 0xe1, 0x9b, 0x87, 0xe1, 0x9b, 0x95, 0xc2, 0xa5, 0xc9, 0x86, 0xc7, - 0xb5, 0xc2, 0xaf, 0xe1, 0x9b, 0x9a, 0xc4, 0xa9, 0xc3, 0x8a, 0xc9, 0x80, 0xc3, 0xaf, - 0xc6, 0x8f, 0xc6, 0x89, 0xc4, 0xa9, 0xc4, 0xb4, 0xe1, 0x9b, 0x8d, 0x4b, 0xc8, 0x96, - 0xc6, 0x87, 0x40, 0xc4, 0x8e, 0xc6, 0x81, 0xc3, 0x9b, 0xc8, 0x9b, 0x49, 0xe2, 0xb1, - 0xa0, 0xc4, 0xb4, 0xc6, 0xa8, 0xc6, 0x8c, 0xc3, 0xb7, 0x34, 0xc8, 0x89, 0xc2, 0xb8, - 0xc2, 0xb2, 0xc4, 0xa8, 0xc4, 0xa0, 0xc7, 0xbb, 0xe2, 0xb1, 0xac, 0xe1, 0x9b, 0x97, - 0xc3, 0xa9, 0xc2, 0xbe, 0xc6, 0x85, 0xc2, 0xa9, 0xcd, 0xb5, 0xc3, 0x95, 0xc8, 0x8b, - 0xc5, 0x94, 0xe2, 0xb1, 0xa0, 0xc2, 0xb5, 0xc6, 0x94, 0xc4, 0xba, 0xe2, 0xb1, 0xbb, - 0xe1, 0x9b, 0x88, 0xc4, 0xa2, 0xc5, 0xa2, 0xcd, 0xbc, 0xc6, 0x88, 0xc3, 0x80, 0xc8, - 0x9d, 0xc5, 0x9b, 0xc7, 0xbe, 0xc5, 0x8c, 0xc5, 0xa8, 0xc5, 0xb0, 0xc9, 0x8c, 0xc2, - 0xa5, 0xc7, 0xbd, 0xc8, 0xaf, 0xc3, 0xb2, 0xc7, 0x8b, 0xc2, 0xb8, 0x25, 0xe1, 0x9b, - 0x80, 0xc9, 0x8f, 0xe1, 0x9b, 0xaa, 0xc5, 0xb1, 0xe1, 0x9b, 0x9d, 0xc5, 0xa5, 0xc2, - 0xa3, 0xc8, 0xaf, 0xc8, 0xb1, 0xc6, 0xb3, 0x3f, 0xc6, 0xb9, 0xc7, 0x91, 0xc7, 0x81, - 0xc7, 0xbc, 0x5d, 0x7c, 0x69, 0xc7, 0x96, 0xe1, 0x9b, 0x8c, 0xc7, 0xb7, 0xc6, 0xa3, - 0xe2, 0xb1, 0xb0, 0xc6, 0xb7, 0xce, 0x88, 0xc9, 0x81, 0xc3, 0x80, 0xe1, 0x9b, 0xaf, - 0xc7, 0xae, 0xc5, 0xbc, 0xc4, 0x8c, 0xc4, 0xa7, 0x71, 0x6e, 0xc6, 0x91, 0xc4, 0x96, - 0xc6, 0x88, 0xc6, 0x97, 0xc5, 0xa4, 0xc5, 0xbf, 0xc7, 0x8d, 0xc8, 0xb9, 0xc7, 0xb5, - 0xe1, 0x9a, 0xb0, 0xc4, 0xa8, 0xc7, 0xa3, 0xc6, 0xba, 0xe1, 0x9a, 0xb5, 0xc6, 0x86, - 0xe1, 0x9b, 0x80, 0xc9, 0x82, 0x4a, 0xc7, 0xa3, 0x7e, 0xc3, 0x9f, 0xc8, 0xb9, 0x7a, - 0x3b, 0xc6, 0xa7, 0xc8, 0x97, 0xc6, 0x92, 0xe1, 0x9b, 0xa1, 0xc7, 0x97, 0xc7, 0xa7, - 0xe1, 0x9a, 0xa3, 0xc9, 0x8d, 0xc6, 0xa0, 0xe1, 0x9b, 0xa0, 0xc4, 0x90, 0xc3, 0x9a, - 0xc4, 0x8f, 0xc7, 0xbc, 0xc4, 0x8b, 0xc2, 0xaa, 0x5d, 0xc3, 0xb0, 0xc3, 0x8b, 0xc4, - 0x90, 0xc3, 0xac, 0xc4, 0xad, 0xe1, 0x9a, 0xb8, 0xc5, 0x93, 0xc8, 0x92, 0xc3, 0x81, - 0xe1, 0x9a, 0xbc, 0xc4, 0xb3, 0xc6, 0x83, 0xc8, 0x9b, 0xc8, 0x81, 0x45, 0xe1, 0x9b, - 0x89, 0xc7, 0xbb, 0xc9, 0x87, 0xc6, 0xb6, 0x4c, 0xc6, 0x88, 0x60, 0xe1, 0x9b, 0x8c, - 0xc6, 0xa7, 0xcd, 0xbc, 0xc3, 0xbe, 0xc2, 0xb3, 0xc7, 0xab, 0xcd, 0xb6, 0xc8, 0xa2, - 0xc2, 0xbc, 0xe2, 0xb1, 0xa5, 0xc5, 0x8f, 0xc6, 0xa0, 0xc6, 0xa6, 0xc6, 0x9b, 0xc8, - 0x94, 0xe1, 0x9a, 0xa0, 0xe2, 0xb1, 0xb6, 0x3f, 0xcd, 0xb2, 0xc8, 0x81, 0xc3, 0x88, - 0xc3, 0xb9, 0xe1, 0x9b, 0x81, 0xe2, 0xb1, 0xa8, 0xc5, 0xb8, 0x48, 0x60, 0xc6, 0xb0, - 0xc5, 0xb1, 0xc3, 0x8e, 0x4a, 0xc5, 0x84, 0xc8, 0xaf, 0xc4, 0xb2, 0xc4, 0x97, 0xe2, - 0xb1, 0xbd, 0xc4, 0xb6, 0x3f, 0xc8, 0x89, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0xcc, 0x07, 0x9a, 0xf8, 0x84, 0x69, 0xe9, 0xbd, 0x7e, 0x67, 0x8e, 0xcf, 0x7f, 0x9f, - 0x79, 0xfa, 0x4d, 0x30, 0x89, 0x27, 0x28, 0xad, 0x87, 0x32, 0xe9, 0xcc, 0x99, 0x7d, - 0x3c, 0x7c, 0x64, 0x1f, + 0x76, 0x3d, 0x56, 0xa8, 0x53, 0xf6, 0xac, 0x5e, 0xe8, 0xef, 0x29, 0x4e, 0xfd, 0x01, + 0x42, 0xbe, 0x6c, 0x23, 0xc5, 0x18, 0x96, 0xfc, 0xb8, 0x3c, 0x40, 0x09, 0x4d, 0x34, + 0x01, 0xc0, 0xc0, 0x94, ], }, TestVector { key: [ - 0x49, 0x26, 0x53, 0x80, 0xd2, 0xb0, 0x2e, 0x0a, 0x1d, 0x98, 0x8f, 0x3d, 0xe3, 0x45, - 0x8b, 0x6e, 0x00, 0x29, 0x1d, 0xb0, 0xe6, 0x2e, 0x17, 0x47, 0x91, 0xd0, 0x09, 0x29, - 0x9f, 0x61, 0xfe, 0xc4, + 0xec, 0x05, 0xbb, 0x7f, 0x06, 0x5e, 0x25, 0x6f, 0xf4, 0x54, 0xf8, 0xa8, 0xdf, 0x6f, + 0x2f, 0x9b, 0x8a, 0x8c, 0x95, 0x08, 0xca, 0xac, 0xfe, 0xe9, 0x52, 0x1c, 0xbe, 0x68, + 0x9d, 0xd1, 0x12, 0x0f, ], description: [ - 0xc7, 0x88, 0xc2, 0xa3, 0xe2, 0xb1, 0xa7, 0xc5, 0x84, 0xc6, 0xac, 0xc8, 0xaa, 0x63, - 0xc6, 0xa7, 0xc5, 0xa7, 0x75, 0xc7, 0x97, 0xe1, 0x9b, 0xaf, 0x71, 0xc8, 0x95, 0xe1, - 0x9a, 0xba, 0x44, 0x45, 0xc3, 0xa1, 0xe1, 0x9b, 0xac, 0xc6, 0xbf, 0xc4, 0xbc, 0xc4, - 0x88, 0xe1, 0x9b, 0x82, 0xe2, 0xb1, 0xb1, 0x5d, 0xc3, 0xa7, 0xe1, 0x9b, 0xa0, 0xc7, - 0xb4, 0x5a, 0xc4, 0xad, 0xe1, 0x9b, 0x8f, 0xc5, 0x88, 0xc9, 0x8c, 0xc8, 0xab, 0xe1, - 0x9a, 0xaf, 0xc7, 0xb6, 0xc2, 0xb8, 0x59, 0xc5, 0xbb, 0xc5, 0xbd, 0x2e, 0xc5, 0x86, - 0xe2, 0xb1, 0xbb, 0xe2, 0xb1, 0xb1, 0xc3, 0xb4, 0xc5, 0xb2, 0xc4, 0x96, 0xc5, 0x99, - 0xc7, 0x8c, 0x71, 0xc6, 0xa2, 0xc3, 0x91, 0xc5, 0xb9, 0xcd, 0xbe, 0x26, 0xe1, 0x9a, - 0xa8, 0xe1, 0x9b, 0x86, 0xc3, 0x99, 0xc8, 0xa7, 0xc4, 0x8b, 0xe2, 0xb1, 0xb4, 0xc5, - 0x8c, 0xc5, 0x9e, 0xc5, 0xa5, 0xc4, 0xbd, 0x3c, 0xc5, 0x89, 0xc4, 0x88, 0x29, 0xc2, - 0xa6, 0x2c, 0xc7, 0xa9, 0xc5, 0xb6, 0xc8, 0x92, 0xc5, 0xb8, 0x3c, 0xc6, 0x8b, 0xc6, - 0x83, 0xe1, 0x9b, 0x9b, 0xc6, 0x88, 0xe2, 0xb1, 0xba, 0x32, 0x63, 0xc7, 0xbe, 0xc6, - 0x8a, 0xc7, 0x8d, 0xc6, 0xa5, 0xc6, 0x9f, 0xc8, 0x98, 0xc4, 0xb0, 0xc8, 0xac, 0xc8, - 0xb1, 0xc5, 0xa3, 0xc2, 0xb1, 0xc9, 0x8f, 0xc4, 0xbf, 0xe1, 0x9b, 0xa3, 0xe2, 0xb1, - 0xad, 0xc4, 0x88, 0xc5, 0x8d, 0xc7, 0x98, 0xc8, 0xb4, 0xc3, 0xab, 0xc3, 0x9b, 0xc6, - 0xa5, 0xe1, 0x9b, 0x89, 0xc2, 0xa8, 0xce, 0x88, 0xe2, 0xb1, 0xb9, 0xe1, 0x9b, 0x83, - 0xc6, 0x83, 0xce, 0x8c, 0xc3, 0xbc, 0x65, 0xe1, 0x9a, 0xaf, 0xc4, 0x88, 0xe1, 0x9a, - 0xa1, 0xc8, 0x87, 0xc8, 0x98, 0xc5, 0x8e, 0xc8, 0xbd, 0x30, 0xc4, 0xb8, 0xc7, 0xa4, - 0x48, 0xc8, 0xa1, 0xe1, 0x9b, 0x93, 0xc4, 0xa8, 0xc5, 0xba, 0x52, 0xc3, 0xbc, 0xc6, - 0xbe, 0xc6, 0x96, 0xc9, 0x8c, 0x3a, 0xc8, 0xa9, 0xc2, 0xac, 0xe1, 0x9b, 0x8c, 0xc3, - 0x9d, 0xe2, 0xb1, 0xb0, 0xc4, 0xaf, 0x34, 0xc5, 0xb2, 0xc7, 0xb3, 0xc6, 0xb2, 0xe1, - 0x9a, 0xb9, 0xc7, 0x98, 0xc8, 0xbe, 0x7a, 0xc2, 0xa7, 0xe1, 0x9b, 0x85, 0xc9, 0x8b, - 0xc5, 0x98, 0xc7, 0xa3, 0xe2, 0xb1, 0xab, 0xc3, 0xbf, 0xc3, 0xb1, 0xc8, 0x88, 0xc4, - 0x9d, 0xc4, 0x80, 0xc7, 0xac, 0xc7, 0xbe, 0xe1, 0x9a, 0xa3, 0xc6, 0x8c, 0xe1, 0x9b, - 0xa5, 0xc8, 0x8f, 0xc4, 0xbf, 0xc4, 0xbf, 0xc8, 0x84, 0xc3, 0x81, 0xc7, 0xa6, 0xe2, - 0xb1, 0xa3, 0xc3, 0xb2, 0x5a, 0xe1, 0x9b, 0xa2, 0xe1, 0x9a, 0xba, 0xc4, 0x92, 0xc8, - 0xaa, 0xe1, 0x9a, 0xaf, 0xc5, 0x8b, 0xc6, 0xa3, 0x73, 0xc6, 0x87, 0xc4, 0x82, 0xc8, - 0xa0, 0xc8, 0xaf, 0x6f, 0xe2, 0xb1, 0xbb, 0xc7, 0x9c, 0x55, 0xe1, 0x9a, 0xbf, 0xc2, - 0xa6, 0xc8, 0xab, 0xc6, 0xa5, 0xc8, 0x8b, 0xc5, 0x90, 0xc5, 0xbf, 0xc8, 0x94, 0xc6, - 0xa3, 0xc4, 0xbe, 0xc4, 0xab, 0xc7, 0xb8, 0xc4, 0x91, 0xc6, 0xa6, 0xc5, 0xb6, 0xc3, - 0x9b, 0xc8, 0xae, 0xc4, 0x8f, 0x62, 0xc2, 0xa4, 0xc8, 0x99, 0xcd, 0xba, 0xc4, 0x96, - 0xc2, 0xb2, 0xc6, 0x92, 0xc5, 0xb5, 0xc5, 0x83, 0xc6, 0x92, 0xc6, 0x8d, 0xe1, 0x9b, - 0xb0, 0xe1, 0x9b, 0x99, 0xc7, 0xae, 0xe1, 0x9a, 0xa7, 0x64, 0x33, 0x3c, 0xc6, 0xab, - 0xc4, 0x95, 0xc3, 0xbd, 0xc8, 0xad, 0xc5, 0xa5, 0x5e, 0xe2, 0xb1, 0xbe, 0xe1, 0x9a, - 0xb6, 0xc8, 0x83, 0x54, 0x25, 0xc8, 0x80, 0xc6, 0xa9, 0xe1, 0x9a, 0xb2, 0xc5, 0x98, - 0xc8, 0xb3, 0xc6, 0x96, 0xc7, 0x87, 0xe1, 0x9a, 0xaa, 0xe1, 0x9b, 0x92, 0xc6, 0xaa, - 0x72, 0x67, 0xe2, 0xb1, 0xb4, 0x4d, 0xc3, 0xb2, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0xaa, 0xb9, 0x9e, 0x01, 0x44, 0xfb, 0xf5, 0x9f, 0x87, 0xd2, 0xe5, 0xf6, 0x32, 0x63, - 0x82, 0xf6, 0x3a, 0x46, 0x83, 0x83, 0xb4, 0x74, 0x40, 0xd7, 0x84, 0xe9, 0x4e, 0x6b, - 0x3a, 0xb6, 0x45, 0x28, + 0x90, 0x10, 0xb3, 0x4c, 0xdf, 0xfd, 0x95, 0x84, 0x96, 0x62, 0x44, 0xdd, 0x6c, 0x3a, + 0x23, 0x4f, 0xc6, 0xa0, 0x74, 0xf1, 0xa9, 0xc9, 0xb2, 0xd8, 0x3d, 0x1c, 0x2c, 0x04, + 0xf5, 0x30, 0x75, 0x0c, ], }, TestVector { key: [ - 0x9a, 0x0e, 0x46, 0x39, 0xb4, 0x69, 0x1f, 0x02, 0x7c, 0x0d, 0xb7, 0xfe, 0xf1, 0xbb, - 0x5e, 0xf9, 0x0a, 0xcd, 0xb7, 0x08, 0x62, 0x6d, 0x2e, 0x1f, 0x3e, 0x38, 0x3e, 0xe7, - 0x5b, 0x31, 0xcf, 0x57, + 0xb0, 0xfa, 0x9d, 0x77, 0xfc, 0xbd, 0x96, 0x45, 0x91, 0x32, 0xe3, 0x05, 0xe3, 0x24, + 0xe7, 0x93, 0x6a, 0xe1, 0x3b, 0x15, 0x14, 0x7e, 0x20, 0x5d, 0x7b, 0xae, 0x42, 0xfa, + 0x7f, 0xaf, 0x5d, 0x1e, ], description: [ - 0xc3, 0xaa, 0xc3, 0xa0, 0xe2, 0xb1, 0xba, 0xc6, 0x8d, 0x34, 0xc4, 0xb0, 0xc4, 0x8b, - 0xc2, 0xba, 0xc5, 0xa9, 0xe2, 0xb1, 0xa6, 0xc3, 0xa8, 0x6f, 0x2c, 0x45, 0xc5, 0xaf, - 0xc4, 0xbb, 0xc7, 0x8b, 0xc6, 0x8e, 0xc8, 0x94, 0xc3, 0xac, 0xc2, 0xab, 0xc8, 0x8b, - 0xe1, 0x9a, 0xb4, 0xc5, 0x9d, 0xc8, 0xb3, 0xc7, 0xb9, 0xe1, 0x9a, 0xac, 0xc6, 0x9b, - 0xc4, 0x9d, 0xc2, 0xa3, 0xe1, 0x9a, 0xb6, 0xc5, 0x92, 0xc7, 0x87, 0x46, 0xc4, 0x99, - 0xce, 0x88, 0xc5, 0x91, 0xc8, 0x92, 0xc4, 0xa9, 0xc3, 0xab, 0x78, 0xc9, 0x8e, 0xc7, - 0xac, 0xc5, 0xba, 0xc4, 0x88, 0xc5, 0x80, 0xc7, 0x91, 0xc5, 0xb7, 0xc5, 0xaa, 0xe1, - 0x9b, 0x97, 0x30, 0xc8, 0x8d, 0x5c, 0xc2, 0xb3, 0xc4, 0x9f, 0x2a, 0xc3, 0xb8, 0xc7, - 0xae, 0xc7, 0xbd, 0xc4, 0xa0, 0xe1, 0x9a, 0xa1, 0xe1, 0x9a, 0xbf, 0xc7, 0x9e, 0xe1, - 0x9b, 0x84, 0xc8, 0x9c, 0xc5, 0x86, 0x2b, 0xc4, 0x96, 0xcd, 0xb4, 0xc3, 0x82, 0x6a, - 0xc8, 0xa6, 0xc5, 0x8c, 0xc3, 0x8a, 0xc2, 0xbf, 0xc6, 0x9e, 0x32, 0xe2, 0xb1, 0xa0, - 0xc5, 0xa2, 0xc8, 0x8b, 0x3e, 0xc4, 0x91, 0xc6, 0xab, 0xc3, 0x92, 0xc7, 0xa7, 0xc8, - 0xa3, 0xc4, 0xa7, 0xe1, 0x9a, 0xb0, 0xc4, 0x9c, 0xcd, 0xb6, 0xe1, 0x9a, 0xa1, 0xc6, - 0x8b, 0xe1, 0x9b, 0x9e, 0xc2, 0xb5, 0xc6, 0x89, 0x33, 0x46, 0xc6, 0x8d, 0xc4, 0xb3, - 0xc8, 0xac, 0xc5, 0x97, 0xc3, 0xb7, 0xc7, 0xab, 0xe2, 0xb1, 0xa1, 0xe1, 0x9b, 0x9f, - 0xc4, 0x94, 0xe1, 0x9b, 0xb0, 0xc8, 0x8e, 0xc4, 0xa8, 0xc5, 0x8b, 0xc6, 0xbb, 0xc2, - 0xa6, 0xe1, 0x9b, 0xae, 0xc2, 0xac, 0xe1, 0x9b, 0x84, 0xc2, 0xb1, 0x4c, 0xc2, 0xb7, - 0xc5, 0x9c, 0x5d, 0xc9, 0x8c, 0xe1, 0x9b, 0x8b, 0x31, 0xc6, 0xb5, 0x62, 0xc7, 0x9e, - 0xc3, 0xa8, 0xc8, 0x82, 0xc7, 0xb7, 0x69, 0xe1, 0x9a, 0xbf, 0xe1, 0x9a, 0xa3, 0xe1, - 0x9b, 0xb0, 0x6e, 0xc6, 0xb9, 0xc9, 0x89, 0xc8, 0xb1, 0xc6, 0xaf, 0xe1, 0x9b, 0x90, - 0x55, 0xc4, 0xb1, 0xe1, 0x9b, 0xac, 0xe1, 0x9b, 0x8b, 0xc7, 0xaa, 0xe1, 0x9b, 0x88, - 0xc3, 0xbf, 0xe1, 0x9a, 0xa5, 0xc5, 0xa9, 0xc2, 0xb7, 0x62, 0xc3, 0x91, 0xc5, 0x93, - 0xc4, 0xbd, 0xc3, 0x90, 0xe1, 0x9b, 0x86, 0xe1, 0x9b, 0x8e, 0xcd, 0xbc, 0xc8, 0x86, - 0xe2, 0xb1, 0xb2, 0xc2, 0xbe, 0xc6, 0xbd, 0x64, 0xc2, 0xb2, 0xcd, 0xbb, 0xc3, 0x90, - 0xc7, 0x9c, 0xcd, 0xb5, 0xc8, 0x9f, 0xc7, 0xa0, 0x75, 0xc8, 0x9f, 0xe1, 0x9b, 0x99, - 0xc5, 0x9a, 0xe2, 0xb1, 0xae, 0xc6, 0x90, 0xc4, 0xad, 0xc3, 0x89, 0xc2, 0xba, 0xc4, - 0x91, 0xc6, 0x83, 0xe1, 0x9a, 0xa6, 0xc3, 0x93, 0xc3, 0x9b, 0xc7, 0x8f, 0xc4, 0xa3, - 0xc5, 0xae, 0x40, 0xc5, 0xb4, 0xe1, 0x9b, 0x97, 0xe2, 0xb1, 0xbf, 0xc6, 0xb3, 0xe2, - 0xb1, 0xb2, 0xe1, 0x9a, 0xae, 0xc3, 0x95, 0xc4, 0x80, 0xc5, 0x9b, 0xc5, 0xa9, 0xc5, - 0xa8, 0xe1, 0x9b, 0xa7, 0xc8, 0x98, 0x75, 0x53, 0xe2, 0xb1, 0xb4, 0xe1, 0x9a, 0xb4, - 0xc3, 0xa3, 0x4b, 0xc3, 0x86, 0xe1, 0x9a, 0xa9, 0xc3, 0x80, 0xe1, 0x9b, 0xb0, 0xc3, - 0x81, 0xc4, 0x86, 0xc9, 0x83, 0xc8, 0xbb, 0xc3, 0x88, 0xc8, 0x96, 0xc7, 0xac, 0xc4, - 0x9a, 0xc5, 0xb7, 0x7e, 0xc7, 0xbe, 0xc6, 0xba, 0xc2, 0xb6, 0xe1, 0x9a, 0xb5, 0xc7, - 0xbd, 0xc7, 0x80, 0x73, 0xc7, 0xab, 0xc8, 0xbd, 0xc7, 0xb7, 0xc3, 0x89, 0xe1, 0x9b, - 0x92, 0xe1, 0x9b, 0x85, 0xe2, 0xb1, 0xb7, 0xc8, 0x8c, 0xc4, 0xb8, 0xc8, 0xa2, 0xc3, - 0xa6, 0xc4, 0x9c, 0xc5, 0x9b, 0xc2, 0xa3, 0xc8, 0x8b, 0xc2, 0xbd, 0xe2, 0xb1, 0xb0, - 0xc5, 0xa6, 0xce, 0x87, 0xc2, 0xb3, 0xc5, 0xbc, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x9c, 0x67, 0xc9, 0x2b, 0xeb, 0x8d, 0x82, 0xb5, 0x8a, 0x3d, 0x24, 0xba, 0xaa, 0x0a, - 0xa1, 0x5c, 0x30, 0x64, 0x16, 0xb2, 0x63, 0x72, 0x16, 0xe9, 0xb7, 0x83, 0x05, 0x43, - 0x4c, 0x57, 0x96, 0xb6, + 0xbd, 0x61, 0xcd, 0xd1, 0x35, 0x54, 0x21, 0x00, 0x57, 0xaa, 0xef, 0xc0, 0xf0, 0xc7, + 0xc0, 0xbe, 0x9c, 0xdb, 0xef, 0x14, 0xe8, 0x79, 0xac, 0x10, 0xf3, 0xd2, 0x66, 0xad, + 0x18, 0x39, 0xc2, 0x1b, ], }, TestVector { key: [ - 0xbb, 0xf4, 0x49, 0x82, 0xf1, 0xba, 0x3a, 0x2b, 0x9d, 0xd3, 0xc1, 0x77, 0x4d, 0x71, - 0xce, 0x33, 0x60, 0x59, 0x9b, 0x07, 0xf2, 0x11, 0xc8, 0x16, 0xb8, 0xc4, 0x3b, 0x98, - 0x42, 0x23, 0x09, 0x24, + 0x81, 0x8f, 0x50, 0xce, 0x47, 0x10, 0xf4, 0xeb, 0x11, 0xe7, 0x43, 0xe6, 0x40, 0x85, + 0x44, 0xaa, 0x3c, 0x12, 0x3c, 0x7f, 0x07, 0xe2, 0xaa, 0xbb, 0x91, 0xaf, 0xc4, 0xec, + 0x48, 0x78, 0x8d, 0xe9, ], description: [ - 0xc4, 0xa8, 0xc5, 0x96, 0xc2, 0xb6, 0xc6, 0xb1, 0x7c, 0xc7, 0xa0, 0x36, 0xc6, 0x9f, - 0x75, 0xc2, 0xa1, 0xc3, 0xbe, 0xc8, 0x90, 0xc2, 0xa6, 0xc3, 0x86, 0xc9, 0x86, 0xe2, - 0xb1, 0xa7, 0xe1, 0x9b, 0xa8, 0xc5, 0x9c, 0xc7, 0xa6, 0xc7, 0xbd, 0xe1, 0x9b, 0xa2, - 0x37, 0xc2, 0xbd, 0xc3, 0x97, 0xc4, 0x8f, 0xc3, 0x80, 0xc4, 0x8f, 0xc6, 0xa6, 0xc5, - 0x8e, 0xce, 0x89, 0xc4, 0xa6, 0xe1, 0x9a, 0xa7, 0xe1, 0x9b, 0x8f, 0xe2, 0xb1, 0xb7, - 0xc9, 0x84, 0xe1, 0x9a, 0xa1, 0xc6, 0x97, 0xe1, 0x9b, 0x85, 0xc3, 0xaf, 0xc6, 0xbf, - 0x3e, 0xe2, 0xb1, 0xbf, 0xc7, 0xa9, 0x39, 0xc4, 0x80, 0xc3, 0x80, 0xc6, 0x87, 0xc3, - 0xa9, 0x4c, 0x66, 0xc8, 0xa5, 0x3b, 0xc5, 0x83, 0x48, 0xc6, 0xbd, 0xc5, 0xbd, 0xc7, - 0x8f, 0xc8, 0xaf, 0xc7, 0x9c, 0xe1, 0x9a, 0xb5, 0xcd, 0xbc, 0xc5, 0x9a, 0xc2, 0xa1, - 0xe2, 0xb1, 0xbf, 0xe1, 0x9b, 0x91, 0xc4, 0x8d, 0xc5, 0x9b, 0xc3, 0x9d, 0xc5, 0x90, - 0xe1, 0x9b, 0x82, 0x71, 0xc2, 0xb9, 0xc7, 0xa4, 0xcd, 0xbc, 0xc5, 0x80, 0xc8, 0x8e, - 0xc7, 0x83, 0x53, 0xc8, 0xb1, 0xc4, 0x83, 0xe2, 0xb1, 0xa0, 0xc5, 0x86, 0xc5, 0xa7, - 0xc4, 0xb0, 0xc6, 0x9b, 0xc8, 0xa6, 0xc8, 0xb2, 0xc5, 0x89, 0xc8, 0xb7, 0xc9, 0x83, - 0xc5, 0xaa, 0xc3, 0x9a, 0xc4, 0xae, 0xc4, 0xbb, 0xc6, 0x9f, 0xc6, 0xac, 0xc8, 0x8b, - 0xc9, 0x8e, 0x70, 0xc5, 0x88, 0x6e, 0xc6, 0x9e, 0xe1, 0x9a, 0xb7, 0xcd, 0xb5, 0xc3, - 0x9d, 0xe2, 0xb1, 0xb2, 0xc4, 0x90, 0xe1, 0x9b, 0x97, 0xc7, 0xb0, 0xc6, 0xa0, 0xe2, - 0xb1, 0xaa, 0xc7, 0x8e, 0xc5, 0xb4, 0x3c, 0xc7, 0xa8, 0xc9, 0x81, 0xc2, 0xb6, 0xc5, - 0xa8, 0x21, 0x78, 0xe2, 0xb1, 0xab, 0xce, 0x84, 0xc5, 0x85, 0xc6, 0x83, 0xc4, 0x82, - 0xc7, 0x90, 0xc6, 0xb5, 0xc6, 0xb9, 0xc8, 0xac, 0xc7, 0xb9, 0xc3, 0xb1, 0xc5, 0x85, - 0xe1, 0x9a, 0xa5, 0x50, 0x44, 0xc5, 0xb7, 0xe1, 0x9a, 0xac, 0xc4, 0x9c, 0xc7, 0x90, - 0xc5, 0x85, 0xc8, 0xbe, 0xc2, 0xb5, 0xc6, 0xb7, 0xc7, 0xba, 0xc6, 0x98, 0xe1, 0x9b, - 0xa8, 0xe2, 0xb1, 0xb9, 0xc5, 0xae, 0xc7, 0x83, 0xc5, 0xb4, 0xc4, 0x98, 0xc6, 0x9f, - 0xc5, 0x84, 0xc3, 0x92, 0xe1, 0x9b, 0x85, 0xc5, 0x95, 0x31, 0xc3, 0x8e, 0xe1, 0x9a, - 0xa0, 0x66, 0xc8, 0xbf, 0xc6, 0x96, 0xc7, 0xbb, 0xc2, 0xb2, 0xe2, 0xb1, 0xbf, 0xc4, - 0x96, 0xe1, 0x9a, 0xb6, 0xc8, 0xa5, 0xc5, 0xb6, 0xc6, 0xa7, 0xc8, 0x8d, 0xc3, 0xa0, - 0xc7, 0xae, 0xc7, 0xb8, 0xe1, 0x9b, 0x85, 0xc7, 0x8a, 0xc4, 0xa4, 0xc4, 0x9d, 0xc4, - 0xbe, 0xe1, 0x9a, 0xb5, 0xc4, 0x85, 0xc9, 0x83, 0xc3, 0xb5, 0xc3, 0xba, 0xc4, 0x9d, - 0xc6, 0xad, 0x68, 0xe1, 0x9b, 0xb0, 0xc4, 0x86, 0xc7, 0xb6, 0xc6, 0x92, 0xc9, 0x80, - 0xc5, 0x80, 0xcd, 0xb1, 0xc7, 0x85, 0xc4, 0xae, 0xc6, 0x95, 0xe2, 0xb1, 0xb2, 0xc5, - 0x9d, 0xc8, 0x99, 0xc4, 0x91, 0xe1, 0x9b, 0x93, 0xe1, 0x9b, 0x91, 0xc4, 0xbc, 0xc4, - 0x85, 0xe1, 0x9b, 0xa1, 0xc5, 0x86, 0x26, 0xc3, 0xa8, 0xc3, 0x88, 0xc4, 0xb8, 0xe2, - 0xb1, 0xa7, 0xc2, 0xa5, 0xe1, 0x9b, 0x98, 0xc4, 0xb3, 0x7c, 0xc3, 0xb1, 0xe1, 0x9b, - 0x94, 0xc6, 0x9b, 0xcd, 0xbc, 0xcd, 0xb2, 0xc8, 0x8c, 0xc6, 0x8c, 0xe2, 0xb1, 0xb0, - 0x52, 0xc3, 0x9b, 0xc3, 0x8c, 0x5e, 0xc5, 0x90, 0xc3, 0xa4, 0xc3, 0x8a, 0x58, 0xc8, - 0x9e, 0xc5, 0xba, 0xc5, 0xb3, 0x6c, 0xc2, 0xab, 0xc4, 0xa1, 0x38, 0xc6, 0xb6, 0xe1, - 0x9b, 0x9f, 0x71, 0xc9, 0x84, 0xe2, 0xb1, 0xb9, 0xc3, 0x9a, 0xc6, 0x83, 0xe1, 0x9a, - 0xa4, 0xc6, 0x85, 0xc3, 0x8c, 0x25, 0x4e, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x08, 0x32, 0x00, 0xcc, 0x9c, 0xa5, 0x39, 0xbe, 0xc9, 0x29, 0x51, 0x50, 0x4d, 0xca, - 0xf2, 0xc5, 0xe0, 0xe8, 0xe3, 0x57, 0xd7, 0xea, 0xed, 0x18, 0xb0, 0x15, 0x8b, 0x69, - 0x2c, 0xe4, 0xce, 0x9d, + 0xae, 0xc8, 0x2d, 0x62, 0x1d, 0x97, 0xcf, 0x35, 0xfb, 0xef, 0x86, 0x88, 0xc5, 0xc8, + 0x30, 0xa0, 0xfd, 0x9c, 0x4a, 0xf7, 0x37, 0x0a, 0x15, 0xc8, 0x99, 0xb2, 0x84, 0x86, + 0xb3, 0xf0, 0x96, 0x20, ], }, TestVector { key: [ - 0xff, 0x63, 0xc7, 0x89, 0x25, 0x1c, 0x10, 0x43, 0xc6, 0xf9, 0x6c, 0x66, 0xbf, 0x5b, - 0x0f, 0x61, 0xc9, 0xd6, 0x5f, 0xef, 0x5a, 0xaf, 0x42, 0x84, 0xa6, 0xa5, 0x69, 0x94, - 0x94, 0x1c, 0x05, 0xfa, + 0x0b, 0xb4, 0x91, 0x3d, 0xba, 0xf1, 0x4e, 0xf6, 0xd0, 0xad, 0xeb, 0x8b, 0x70, 0x27, + 0xbf, 0x0b, 0x9a, 0x8f, 0x59, 0x0d, 0x3e, 0x2d, 0x95, 0xa1, 0x2d, 0xba, 0xaf, 0x0b, + 0x95, 0x33, 0xdc, 0xa4, ], description: [ - 0xc8, 0x8a, 0xc5, 0xb0, 0xe1, 0x9a, 0xb2, 0xc8, 0xbc, 0xc8, 0xbc, 0xc5, 0x9e, 0x63, - 0xcd, 0xb4, 0xc7, 0xbf, 0xc6, 0xbd, 0xc8, 0x99, 0xc3, 0x8c, 0xc8, 0x9c, 0x45, 0xc9, - 0x85, 0xcd, 0xb3, 0xc6, 0xb8, 0xc8, 0x91, 0x4e, 0xc9, 0x82, 0xc5, 0xab, 0xc5, 0x8a, - 0xce, 0x87, 0xe1, 0x9a, 0xbd, 0xc8, 0xa3, 0xc8, 0xa9, 0xc7, 0x90, 0xc6, 0x9c, 0xe1, - 0x9b, 0xa0, 0x41, 0x4c, 0xc2, 0xbb, 0xc7, 0x93, 0xe1, 0x9a, 0xa3, 0xc5, 0xba, 0xc6, - 0x86, 0xcd, 0xbd, 0xc5, 0x8d, 0xc4, 0xa4, 0xc8, 0xb5, 0xc3, 0x9d, 0xc3, 0xae, 0xc5, - 0x98, 0xe1, 0x9b, 0x91, 0x63, 0x66, 0xc3, 0xbc, 0xc8, 0xae, 0x7e, 0x40, 0xc2, 0xa8, - 0xc3, 0x8a, 0xc4, 0xac, 0x35, 0x3d, 0xcd, 0xbd, 0xc6, 0xad, 0xc3, 0x9b, 0xc6, 0x81, - 0xc5, 0xab, 0xc4, 0x8f, 0x28, 0xc4, 0x9d, 0xe1, 0x9b, 0x8e, 0xe1, 0x9b, 0xa4, 0xc5, - 0x86, 0xe1, 0x9a, 0xba, 0x50, 0xc6, 0xbc, 0xc6, 0x9b, 0xc7, 0xac, 0xc4, 0xb4, 0xe1, - 0x9a, 0xa9, 0xe1, 0x9b, 0xae, 0xc3, 0x91, 0xc6, 0x82, 0xc4, 0xa8, 0xc5, 0xa8, 0xc6, - 0xae, 0x2d, 0xe1, 0x9b, 0x9e, 0xc8, 0x8e, 0xe1, 0x9b, 0xa7, 0xc5, 0x8e, 0xc8, 0x8d, - 0xc3, 0x96, 0xc7, 0x9f, 0xc3, 0xb4, 0x3a, 0xcd, 0xb6, 0xc6, 0x8a, 0x6c, 0xc3, 0x9e, - 0xc9, 0x81, 0x65, 0xe1, 0x9b, 0x93, 0xe1, 0x9b, 0xa2, 0xc7, 0x82, 0xe2, 0xb1, 0xad, - 0xe2, 0xb1, 0xa8, 0xc8, 0x96, 0xc8, 0xaa, 0xc6, 0xbc, 0x6b, 0xe1, 0x9a, 0xb4, 0xc5, - 0x9f, 0xc4, 0x92, 0xc6, 0x8e, 0xc3, 0x80, 0xc5, 0xb8, 0xc3, 0xa4, 0x2d, 0x38, 0xc5, - 0xb9, 0xc7, 0xb7, 0xc3, 0x8f, 0xc2, 0xb3, 0x62, 0xc5, 0x87, 0x21, 0xc6, 0x8f, 0xc7, - 0xb7, 0xe1, 0x9a, 0xb4, 0xc4, 0x84, 0x3e, 0xc7, 0x89, 0xc8, 0xaf, 0xc6, 0x98, 0xc6, - 0xb7, 0xc8, 0x9f, 0xc7, 0x9a, 0xc7, 0x96, 0xc3, 0xbb, 0x55, 0xe2, 0xb1, 0xb5, 0xc5, - 0xad, 0x2e, 0xc3, 0x86, 0xe1, 0x9b, 0x95, 0xc8, 0x81, 0xc3, 0x9b, 0xe1, 0x9b, 0xa4, - 0xcd, 0xb6, 0xc8, 0x8d, 0xc3, 0xa4, 0xc3, 0x89, 0xc8, 0x9d, 0xc8, 0xbe, 0x37, 0x25, - 0xc6, 0x99, 0x32, 0xc6, 0x92, 0xe1, 0x9b, 0x8b, 0x3a, 0x5f, 0x36, 0xc8, 0x9a, 0xc7, - 0xb3, 0x73, 0xc7, 0xaf, 0x7a, 0xc4, 0xac, 0xc7, 0x8b, 0xc8, 0x96, 0xc7, 0x82, 0xc8, - 0x8f, 0xc6, 0xb7, 0x37, 0xc3, 0xbc, 0xc5, 0x89, 0x2c, 0xc9, 0x84, 0xc5, 0xb7, 0xc6, - 0xb9, 0xc6, 0x90, 0xc7, 0x9f, 0xe2, 0xb1, 0xa5, 0xc5, 0x89, 0xc8, 0x8e, 0xc6, 0x8e, - 0xe1, 0x9b, 0x8e, 0xc8, 0xa6, 0xe1, 0x9a, 0xa4, 0xc5, 0xac, 0xc3, 0xa2, 0xc5, 0x9a, - 0x50, 0xc6, 0xa3, 0x44, 0xc6, 0x90, 0xc3, 0xae, 0x60, 0xc3, 0x8c, 0xc4, 0xb9, 0xc4, - 0x94, 0xc4, 0x9f, 0xc4, 0xa3, 0xc8, 0xb6, 0xc2, 0xa8, 0xc2, 0xa5, 0xc5, 0xb4, 0xe2, - 0xb1, 0xa2, 0x3a, 0xc7, 0xbd, 0x78, 0x78, 0xc3, 0xa9, 0xc2, 0xbc, 0xc4, 0xba, 0xc7, - 0x84, 0xc5, 0xb8, 0xe1, 0x9a, 0xaa, 0xe1, 0x9b, 0x83, 0xc3, 0x98, 0xc3, 0xb7, 0xc8, - 0x84, 0xcd, 0xbc, 0xcd, 0xba, 0xc4, 0xbe, 0xc2, 0xa9, 0xe1, 0x9a, 0xa0, 0xc5, 0xbd, - 0x5d, 0xc6, 0x92, 0xc8, 0xa0, 0x42, 0xc3, 0xb8, 0xe1, 0x9b, 0x8e, 0xc9, 0x89, 0xc2, - 0xb6, 0xc2, 0xb6, 0xc5, 0xbc, 0xc3, 0x88, 0xc5, 0x9b, 0xc8, 0xae, 0xc6, 0x98, 0xc7, - 0x9a, 0xc4, 0x82, 0xc6, 0x90, 0xc6, 0xa3, 0xc4, 0x98, 0xc9, 0x8f, 0xe1, 0x9a, 0xb1, - 0xe2, 0xb1, 0xbb, 0xc3, 0x97, 0xc8, 0xba, 0xc8, 0xab, 0xc7, 0xad, 0x4a, 0x3f, 0xe2, - 0xb1, 0xbb, 0xce, 0x8a, 0xc3, 0xa6, 0xc7, 0xbe, 0xc5, 0x8b, 0xe2, 0xb1, 0xbe, 0xc2, - 0xac, 0xc8, 0xbb, 0xc6, 0xb8, 0xc4, 0x8d, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0xf1, 0xfb, 0x77, 0xa6, 0xf4, 0x53, 0x77, 0x23, 0x9c, 0x47, 0x57, 0xff, 0xee, 0xe4, - 0xb2, 0x66, 0x42, 0x1b, 0xfc, 0x50, 0x00, 0xe3, 0x27, 0x68, 0x85, 0x5f, 0xa0, 0x6a, - 0x0f, 0x5d, 0xe2, 0x27, + 0x4b, 0xec, 0xc4, 0x9b, 0x22, 0x54, 0xb4, 0xdd, 0x36, 0x28, 0xac, 0x96, 0xdc, 0xda, + 0x24, 0x70, 0x0e, 0xf8, 0xd7, 0x30, 0x5a, 0x25, 0x4e, 0x8e, 0x0c, 0xa6, 0xce, 0x23, + 0x91, 0x3f, 0xd8, 0x0d, ], }, TestVector { key: [ - 0xbf, 0x39, 0x20, 0xce, 0x2e, 0x9e, 0x95, 0xb0, 0xee, 0xce, 0x13, 0x0a, 0x50, 0xba, - 0x7d, 0xcc, 0x6f, 0x26, 0x51, 0x2a, 0x9f, 0xc7, 0xb8, 0x04, 0xaf, 0xf0, 0x89, 0xf5, - 0x0c, 0xbc, 0xff, 0xf7, + 0xae, 0x36, 0xb6, 0x1a, 0x3d, 0x10, 0xf1, 0xaa, 0x75, 0x2a, 0xb1, 0xdc, 0x16, 0xe3, + 0xe4, 0x9b, 0x6a, 0xc0, 0xd2, 0xae, 0x19, 0x07, 0xd2, 0xe6, 0x94, 0x25, 0xec, 0x12, + 0xc9, 0x3a, 0xae, 0xbc, ], description: [ - 0x2d, 0xc4, 0x8b, 0xc5, 0xa1, 0xc2, 0xa3, 0xc6, 0x99, 0x41, 0xe2, 0xb1, 0xb6, 0x63, - 0x4a, 0xc5, 0xa5, 0xe2, 0xb1, 0xbe, 0xc2, 0xbe, 0xe1, 0x9b, 0x9a, 0xc7, 0xa1, 0xc3, - 0x85, 0xc7, 0xac, 0xc7, 0x99, 0xc3, 0x90, 0xc5, 0x98, 0xc7, 0x9d, 0xc6, 0x8d, 0xc8, - 0xad, 0xc8, 0xbe, 0x61, 0xc3, 0x9d, 0x55, 0x66, 0xc5, 0x89, 0xc6, 0x97, 0xc6, 0xb5, - 0xc3, 0xbe, 0xc4, 0x84, 0xc5, 0x96, 0xc3, 0x8c, 0xc7, 0xa5, 0xe1, 0x9b, 0x94, 0xc2, - 0xab, 0xc3, 0xbd, 0xc6, 0x95, 0x2b, 0xc8, 0x88, 0xc7, 0xa4, 0x68, 0xc8, 0xa5, 0xe1, - 0x9a, 0xbf, 0xc8, 0x84, 0xc4, 0x98, 0xc5, 0xa5, 0xe1, 0x9b, 0x8e, 0xe2, 0xb1, 0xba, - 0x28, 0xc6, 0xab, 0xc8, 0x93, 0xc7, 0xb5, 0xc2, 0xa1, 0xc5, 0x80, 0xc6, 0xb0, 0xc6, - 0x94, 0xc8, 0x84, 0xc2, 0xb7, 0xc5, 0x87, 0xe1, 0x9b, 0x9e, 0xc7, 0x93, 0x6c, 0xe1, - 0x9b, 0xa5, 0x41, 0x62, 0xcd, 0xb1, 0xc7, 0x94, 0xc8, 0xac, 0x3b, 0xc3, 0x95, 0xc4, - 0x83, 0xc6, 0xbb, 0xe2, 0xb1, 0xa2, 0xe1, 0x9a, 0xa6, 0xc9, 0x81, 0xe1, 0x9b, 0x85, - 0xe1, 0x9a, 0xa7, 0xe1, 0x9b, 0x8c, 0xc3, 0xac, 0xc3, 0xa5, 0xc3, 0x80, 0xc3, 0x9d, - 0xc3, 0x9f, 0xc3, 0x96, 0xc3, 0xbb, 0xe2, 0xb1, 0xb4, 0xc8, 0xb1, 0x3b, 0xc5, 0x82, - 0x49, 0xc4, 0x90, 0xc4, 0xbd, 0xc9, 0x81, 0x4e, 0xc5, 0x8e, 0xc4, 0x94, 0xc3, 0xa7, - 0xc9, 0x89, 0xc6, 0x90, 0xc5, 0x8e, 0x3e, 0xe1, 0x9b, 0x99, 0x63, 0xc8, 0xb5, 0xc6, - 0xab, 0x38, 0xc8, 0xaf, 0xc5, 0xad, 0xc8, 0x8e, 0xc5, 0xab, 0xcd, 0xb0, 0xc5, 0xa3, - 0xc3, 0xb7, 0xc7, 0x85, 0xc2, 0xa7, 0x6d, 0xc2, 0xb7, 0x2f, 0xc3, 0xbd, 0xc7, 0x83, - 0xc5, 0xbb, 0xc2, 0xbf, 0xe2, 0xb1, 0xb8, 0xc4, 0xbc, 0xe1, 0x9b, 0xa5, 0xc7, 0x8c, - 0x71, 0x3d, 0xc7, 0xa0, 0xc9, 0x89, 0xc5, 0xb1, 0xc5, 0xa8, 0xc4, 0xa1, 0x5a, 0xe1, - 0x9a, 0xa4, 0xc7, 0xa6, 0xc6, 0x95, 0xc4, 0x90, 0xc7, 0x90, 0xe1, 0x9b, 0x90, 0xc3, - 0x92, 0xe1, 0x9a, 0xbd, 0xc7, 0xb2, 0xc7, 0xa9, 0xc6, 0xbb, 0xc6, 0x83, 0x21, 0xc9, - 0x87, 0xc3, 0x94, 0xc4, 0xac, 0xc2, 0xbf, 0xc4, 0x88, 0xc2, 0xa4, 0xc4, 0xbe, 0xc4, - 0xb2, 0xcd, 0xb6, 0xe2, 0xb1, 0xaa, 0xc9, 0x85, 0xc3, 0x97, 0xc8, 0x80, 0x78, 0xc8, - 0x95, 0xc6, 0x9f, 0xe1, 0x9b, 0xab, 0xc5, 0x8a, 0xc9, 0x82, 0xc8, 0x9a, 0x59, 0xe2, - 0xb1, 0xa7, 0xe1, 0x9b, 0xa2, 0x67, 0xc6, 0xb4, 0xc9, 0x8f, 0xc8, 0xbf, 0xc8, 0x85, - 0xc8, 0xb6, 0xc5, 0x94, 0x68, 0xe1, 0x9a, 0xaf, 0xe2, 0xb1, 0xa6, 0xc8, 0xae, 0xc4, - 0x89, 0xe1, 0x9a, 0xa2, 0xc7, 0xb9, 0xc8, 0xa0, 0xc2, 0xaf, 0xc7, 0x90, 0xc4, 0xb0, - 0xc4, 0xa7, 0xc5, 0xba, 0xc9, 0x82, 0xc3, 0x91, 0xe1, 0x9a, 0xa3, 0xc4, 0x92, 0x54, - 0xe1, 0x9a, 0xaa, 0xe1, 0x9b, 0xa7, 0xe1, 0x9a, 0xac, 0xc4, 0xb5, 0xe1, 0x9b, 0x9c, - 0xc5, 0x91, 0xce, 0x8c, 0xc5, 0xb1, 0x78, 0x66, 0xc5, 0x99, 0xcd, 0xbe, 0xc6, 0xa7, - 0xe1, 0x9b, 0x9e, 0xe2, 0xb1, 0xa4, 0xc3, 0x8e, 0x47, 0xc3, 0xae, 0xce, 0x86, 0x69, - 0xc6, 0x8a, 0xc3, 0x8e, 0xe1, 0x9b, 0xad, 0xc9, 0x88, 0xe1, 0x9b, 0x96, 0x7c, 0xe1, - 0x9b, 0x95, 0xc7, 0xba, 0xc8, 0xab, 0xe1, 0x9b, 0x85, 0xc7, 0x94, 0xc3, 0xa6, 0xe1, - 0x9b, 0xaa, 0x6d, 0xe1, 0x9a, 0xaf, 0xc4, 0x90, 0x79, 0xc3, 0xb5, 0xe2, 0xb1, 0xae, - 0x59, 0xc8, 0xab, 0xc8, 0xb2, 0xc8, 0xad, 0xe2, 0xb1, 0xb5, 0x50, 0xc5, 0xaf, 0x52, - 0xc6, 0xa6, 0xe1, 0x9b, 0x8b, 0xe1, 0x9a, 0xa5, 0x52, 0xcd, 0xbd, 0xc3, 0x86, 0x38, - 0xc4, 0xae, 0xc9, 0x81, 0xc5, 0x96, 0xc6, 0x94, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x10, 0xc3, 0x37, 0x4c, 0xc5, 0xd2, 0x6d, 0x14, 0xda, 0x3d, 0x1d, 0x41, 0x7e, 0x60, - 0x2c, 0x75, 0x2e, 0xfe, 0x36, 0x4f, 0x6c, 0x2f, 0x9e, 0xf3, 0x93, 0x39, 0x1d, 0x1b, - 0x48, 0xd3, 0x35, 0x2c, + 0xfb, 0xd0, 0x1e, 0x66, 0xf8, 0x29, 0xbf, 0xbf, 0x9d, 0x25, 0xd7, 0x36, 0xbc, 0xb3, + 0xa1, 0x04, 0x91, 0x82, 0x81, 0x4e, 0x4d, 0xdf, 0xc2, 0x30, 0x95, 0x63, 0x8f, 0x2f, + 0xf4, 0x3c, 0x15, 0x9b, ], }, TestVector { key: [ - 0x2a, 0x15, 0xb5, 0xe8, 0x6e, 0xe7, 0x0e, 0xe6, 0xab, 0xfb, 0xca, 0x6b, 0x2f, 0x15, - 0x6d, 0x43, 0xf1, 0x85, 0x7d, 0xb5, 0xcb, 0x5e, 0x6d, 0xd6, 0xb7, 0x23, 0x17, 0x20, - 0x05, 0xfc, 0x2f, 0x83, + 0x61, 0xbb, 0x33, 0x91, 0x59, 0xdf, 0x98, 0x20, 0xef, 0xae, 0xb6, 0x1d, 0x9a, 0x10, + 0xcd, 0xc1, 0x3b, 0x4c, 0x99, 0xfd, 0xc8, 0x6d, 0x94, 0x85, 0x11, 0x5d, 0xfd, 0x83, + 0x62, 0x36, 0xac, 0xf8, ], description: [ - 0x2c, 0xc3, 0x8b, 0x50, 0xe1, 0x9a, 0xb0, 0xc8, 0x96, 0xc7, 0xaf, 0x31, 0xc3, 0x9e, - 0xc3, 0x95, 0xc9, 0x87, 0xe1, 0x9b, 0x9f, 0x52, 0xe1, 0x9b, 0x8b, 0xc2, 0xb2, 0xc7, - 0x98, 0xe1, 0x9b, 0x84, 0xc2, 0xa6, 0xc2, 0xaa, 0xc7, 0xaa, 0xc3, 0x80, 0xc7, 0x95, - 0xc3, 0x8e, 0xe2, 0xb1, 0xba, 0x36, 0xc3, 0xa7, 0xc4, 0xa7, 0xe1, 0x9b, 0x8f, 0xe1, - 0x9b, 0x85, 0xc5, 0x87, 0xc7, 0xbb, 0xc5, 0x85, 0xc5, 0x93, 0xc5, 0x9c, 0xc8, 0x92, - 0x6b, 0xe1, 0x9b, 0x96, 0x78, 0xcd, 0xbe, 0xc4, 0x9a, 0xc7, 0x92, 0xc3, 0xb1, 0xc8, - 0xa8, 0xc3, 0x8b, 0x46, 0xc7, 0x94, 0xc5, 0x81, 0xc7, 0xa0, 0xc4, 0xb9, 0x78, 0x49, - 0xc5, 0xbb, 0xc5, 0xb5, 0xc5, 0x86, 0xc5, 0xb7, 0xc7, 0xb9, 0xc4, 0x86, 0xc5, 0xae, - 0xc7, 0xb4, 0x37, 0xc4, 0x89, 0x47, 0xe1, 0x9b, 0x88, 0xc6, 0x9d, 0xc4, 0xa8, 0xe2, - 0xb1, 0xa4, 0xc3, 0x9f, 0x3e, 0xc5, 0xa7, 0xc8, 0xbe, 0x41, 0xc9, 0x8a, 0xe1, 0x9a, - 0xad, 0xc2, 0xba, 0xc3, 0xba, 0xc4, 0x8b, 0xc5, 0x9b, 0xc5, 0xa7, 0x7a, 0xcd, 0xb6, - 0xc8, 0xb5, 0xc7, 0xa0, 0xc4, 0x81, 0xe1, 0x9b, 0x98, 0xc3, 0xb3, 0x72, 0xe1, 0x9a, - 0xad, 0xc6, 0x85, 0xcd, 0xb0, 0xce, 0x89, 0xc8, 0x86, 0xc3, 0x90, 0xc6, 0x83, 0x51, - 0xc7, 0x80, 0xc3, 0x8c, 0xe2, 0xb1, 0xa3, 0xc9, 0x8f, 0xc3, 0x8c, 0xc5, 0xad, 0xc3, - 0x86, 0xe1, 0x9b, 0xa7, 0x54, 0xc3, 0xb0, 0xce, 0x85, 0xc3, 0xbc, 0xe2, 0xb1, 0xa3, - 0xc4, 0x9f, 0x76, 0xc3, 0x91, 0xc5, 0xa0, 0xc4, 0x8f, 0xc5, 0x91, 0xc4, 0xa3, 0x33, - 0xc6, 0x9d, 0xe1, 0x9b, 0x8c, 0xc3, 0xa6, 0xc3, 0xb8, 0xe1, 0x9a, 0xa5, 0xe2, 0xb1, - 0xac, 0xc7, 0x9b, 0xc5, 0xaf, 0xc4, 0xb0, 0xe1, 0x9a, 0xb4, 0x4f, 0xe2, 0xb1, 0xa6, - 0x4e, 0x5f, 0xc8, 0xbf, 0xc3, 0xb3, 0xe1, 0x9b, 0x9a, 0xe1, 0x9a, 0xa4, 0xc7, 0x89, - 0xc6, 0x88, 0xcd, 0xbb, 0xc3, 0xa0, 0xc3, 0xb3, 0xcd, 0xb2, 0xc3, 0xbb, 0xc6, 0xa0, - 0xe1, 0x9b, 0xab, 0xc3, 0xa1, 0xc7, 0x97, 0x32, 0xe1, 0x9b, 0xa5, 0xc7, 0xac, 0xc5, - 0x9c, 0xc8, 0xbf, 0xc4, 0xbc, 0xc4, 0x8a, 0x3a, 0x60, 0xc6, 0x81, 0xe1, 0x9b, 0x80, - 0xc4, 0x82, 0xce, 0x89, 0xe1, 0x9a, 0xa7, 0xe1, 0x9b, 0x9f, 0xc7, 0x92, 0xe2, 0xb1, - 0xa7, 0xe1, 0x9b, 0x82, 0xc7, 0x82, 0xe1, 0x9b, 0x91, 0xc6, 0xb5, 0x5e, 0xc7, 0xb0, - 0x3a, 0xc3, 0xa7, 0xce, 0x89, 0xe1, 0x9a, 0xbf, 0xc4, 0xbe, 0xc5, 0x9e, 0xc5, 0xb0, - 0xe2, 0xb1, 0xb6, 0xc9, 0x8f, 0xc8, 0x94, 0xc3, 0xbd, 0x6b, 0x4c, 0xc3, 0xad, 0xc8, - 0xb3, 0xc4, 0x99, 0xc3, 0xa5, 0xe2, 0xb1, 0xaf, 0xc4, 0x9d, 0x24, 0xc3, 0x91, 0xc5, - 0xa6, 0xc2, 0xaa, 0xe1, 0x9a, 0xb9, 0xc5, 0x9d, 0xc3, 0xab, 0xc5, 0xa3, 0xc6, 0xb6, - 0xe1, 0x9a, 0xac, 0x73, 0x7d, 0x4f, 0xe2, 0xb1, 0xb9, 0xc3, 0x8d, 0xc2, 0xa9, 0x57, - 0x2f, 0xc4, 0x99, 0xc7, 0xa6, 0xc6, 0xbb, 0xe2, 0xb1, 0xa5, 0xc3, 0xb7, 0x70, 0xc6, - 0x8c, 0xc6, 0xa9, 0xc7, 0xbf, 0xc2, 0xa7, 0xc3, 0xa2, 0xc2, 0xb0, 0xc8, 0x81, 0xc3, - 0x81, 0xe1, 0x9a, 0xba, 0xc5, 0x9c, 0xc4, 0x90, 0xc4, 0xa7, 0x71, 0xc8, 0xa9, 0xe1, - 0x9b, 0x9c, 0xe2, 0xb1, 0xab, 0x3e, 0x78, 0xc2, 0xb1, 0xc5, 0x88, 0xc4, 0xb1, 0xc7, - 0x9e, 0x38, 0xc8, 0xac, 0x71, 0xcd, 0xba, 0xc8, 0xba, 0x69, 0xc3, 0x8a, 0xcd, 0xbd, - 0xe1, 0x9b, 0xb0, 0xc8, 0x99, 0xc7, 0xbb, 0xc8, 0xb2, 0xc9, 0x8e, 0xc8, 0xac, 0xc2, - 0xa9, 0xc3, 0xb1, 0x3e, 0xc5, 0xbf, 0xc8, 0x8a, 0xe2, 0xb1, 0xaf, 0xc7, 0xaf, 0xc7, - 0x88, 0x5b, 0x4b, 0xe1, 0x9b, 0x87, 0x5a, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x66, 0x9e, 0x20, 0x80, 0x8b, 0xa2, 0xa4, 0xd1, 0x31, 0xed, 0xeb, 0x7a, 0x39, 0x11, - 0xb1, 0xe1, 0x24, 0xd3, 0x77, 0x56, 0xd5, 0xb1, 0x3c, 0x61, 0xc6, 0x47, 0x73, 0x41, - 0xd2, 0x47, 0x57, 0xb6, + 0xd0, 0x46, 0xef, 0xd5, 0xe2, 0xf1, 0xdd, 0x27, 0x2c, 0x7e, 0x65, 0x8a, 0x72, 0x1d, + 0x36, 0x21, 0x4c, 0x4d, 0xbc, 0x03, 0xf5, 0x99, 0x04, 0x3e, 0x6e, 0xfc, 0xbf, 0xb5, + 0x6a, 0x6e, 0x90, 0x28, ], }, TestVector { key: [ - 0x48, 0xbe, 0xf1, 0xc3, 0x4e, 0x8a, 0x86, 0x4c, 0x7e, 0xaa, 0x0c, 0x85, 0x31, 0x63, - 0x83, 0xc3, 0xf1, 0xc9, 0xb1, 0x87, 0xd5, 0x01, 0x74, 0xb4, 0x9a, 0xfe, 0xbd, 0x99, - 0xc3, 0xa1, 0xaf, 0xf9, + 0x49, 0x26, 0x53, 0x80, 0xd2, 0xb0, 0x2e, 0x0a, 0x1d, 0x98, 0x8f, 0x3d, 0xe3, 0x45, + 0x8b, 0x6e, 0x00, 0x29, 0x1d, 0xb0, 0xe6, 0x2e, 0x17, 0x47, 0x91, 0xd0, 0x09, 0x29, + 0x9f, 0x61, 0xfe, 0xc4, ], description: [ - 0x5d, 0xc7, 0x88, 0xc2, 0xb9, 0x61, 0xc8, 0xb0, 0xc3, 0x98, 0xe2, 0xb1, 0xab, 0xc6, - 0xa9, 0xc8, 0xa7, 0xe2, 0xb1, 0xa9, 0xc5, 0xbb, 0x3a, 0x68, 0xc7, 0x83, 0xc8, 0xa1, - 0xc8, 0xbd, 0xc4, 0x9e, 0x6c, 0xc3, 0xab, 0xe1, 0x9b, 0xaf, 0xe1, 0x9b, 0x83, 0xc6, - 0xb7, 0xc2, 0xa4, 0xc4, 0x81, 0xc7, 0x9c, 0xc5, 0xb2, 0xe1, 0x9a, 0xaf, 0xe1, 0x9a, - 0xab, 0xc5, 0x88, 0xe1, 0x9a, 0xb0, 0xc6, 0xac, 0x63, 0xe1, 0x9b, 0xb0, 0xc7, 0xbe, - 0xc6, 0x8a, 0x3c, 0xc5, 0xb2, 0xc3, 0x89, 0xc2, 0xb0, 0x3d, 0xc2, 0xba, 0xe1, 0x9b, - 0x84, 0xc5, 0x8b, 0xe2, 0xb1, 0xb9, 0xc5, 0x91, 0x39, 0xc6, 0xb6, 0xc7, 0x9e, 0xc8, - 0x81, 0x74, 0xc7, 0xa8, 0xc8, 0xb0, 0xc2, 0xb6, 0xc4, 0x88, 0xc3, 0x9d, 0xc7, 0x89, - 0xc9, 0x8c, 0xc4, 0x85, 0xc3, 0x93, 0xc7, 0xaa, 0xc5, 0xb6, 0xc7, 0x9a, 0xc3, 0xae, - 0xe1, 0x9b, 0x81, 0xc3, 0x96, 0xc5, 0x80, 0xc5, 0x92, 0xc2, 0xa1, 0xc6, 0xa3, 0xe1, - 0x9a, 0xb7, 0xc2, 0xbf, 0xc5, 0xbb, 0xc7, 0xa9, 0xc7, 0x85, 0xc8, 0xbd, 0xc3, 0x8b, - 0xc7, 0xa6, 0xc3, 0xa0, 0xc6, 0x90, 0xe1, 0x9a, 0xb7, 0xc8, 0x88, 0xc4, 0x91, 0xc8, - 0x94, 0xc8, 0x9b, 0x6d, 0xc3, 0xa3, 0xc3, 0xb8, 0xc6, 0x88, 0xc5, 0xb1, 0x6e, 0xc7, - 0x87, 0x40, 0xc6, 0x8f, 0x7a, 0xc8, 0xa8, 0xc3, 0xb4, 0x46, 0xc3, 0xac, 0xc6, 0xa7, - 0xc6, 0x9c, 0xc8, 0x95, 0xc6, 0x87, 0xc6, 0x84, 0xc6, 0xb5, 0xc8, 0x97, 0xc3, 0xb8, - 0x69, 0x48, 0xe1, 0x9a, 0xaf, 0x53, 0xc4, 0xb8, 0xc3, 0x9c, 0xc4, 0xa8, 0xc5, 0xa6, - 0xc2, 0xbd, 0xc4, 0x92, 0xc2, 0xaf, 0xc2, 0xaf, 0xc4, 0xbb, 0xc7, 0x8c, 0xc3, 0xb9, - 0xc6, 0xb5, 0xc8, 0xa2, 0xc5, 0xb1, 0xc3, 0x81, 0xc6, 0x86, 0xc9, 0x8a, 0x4d, 0xc4, - 0xb8, 0xc4, 0xb4, 0xe1, 0x9a, 0xbf, 0xc2, 0xa2, 0xc4, 0x8d, 0xce, 0x87, 0xe2, 0xb1, - 0xbb, 0xe2, 0xb1, 0xa1, 0xc3, 0x87, 0xe1, 0x9a, 0xae, 0xc6, 0xb2, 0xc6, 0xa7, 0xc9, - 0x8d, 0xe1, 0x9b, 0xa8, 0xc4, 0x9a, 0xcd, 0xb1, 0xc5, 0x98, 0xc4, 0xbb, 0xc5, 0x95, - 0xc9, 0x8b, 0x61, 0xe2, 0xb1, 0xbf, 0xc3, 0xa6, 0xc5, 0xbd, 0xc4, 0x9c, 0xce, 0x85, - 0xe2, 0xb1, 0xa0, 0xc5, 0xbf, 0xc5, 0xbf, 0x34, 0xc7, 0x82, 0xcd, 0xb6, 0xe1, 0x9b, - 0x95, 0xc5, 0xaf, 0xc5, 0x98, 0xc8, 0x9f, 0xc2, 0xb8, 0xc4, 0xa5, 0xc5, 0xb8, 0xc4, - 0xad, 0xc8, 0xaf, 0xc6, 0x81, 0x6b, 0xc4, 0x8b, 0x55, 0xc7, 0xbe, 0xc3, 0xb4, 0xc6, - 0xa2, 0x62, 0xc8, 0xa0, 0xc3, 0x8d, 0xc3, 0xa8, 0x75, 0xc5, 0x90, 0xcd, 0xb1, 0x4d, - 0xc7, 0x9a, 0xc5, 0x87, 0xc5, 0xb5, 0xc6, 0x88, 0x25, 0xe2, 0xb1, 0xac, 0xc7, 0xa3, - 0xc9, 0x8d, 0xc8, 0x98, 0x6b, 0xc4, 0x9b, 0xc7, 0x90, 0xc8, 0x8e, 0xc8, 0x86, 0x36, - 0xe1, 0x9a, 0xaa, 0xc4, 0xa8, 0xe1, 0x9a, 0xa9, 0xc2, 0xb9, 0xe1, 0x9a, 0xb6, 0xc2, - 0xa8, 0xc2, 0xa4, 0xc3, 0x9a, 0xc3, 0x86, 0xc4, 0xb4, 0xc7, 0xa1, 0x53, 0xe2, 0xb1, - 0xa5, 0xc5, 0xa1, 0xe1, 0x9b, 0xa2, 0xc5, 0xaa, 0xc8, 0xa1, 0x2d, 0xc8, 0x96, 0x75, - 0xc2, 0xa6, 0xe1, 0x9b, 0x9b, 0xc6, 0xbb, 0xc9, 0x86, 0xc4, 0xa3, 0xc5, 0xb1, 0xe1, - 0x9b, 0x92, 0x39, 0x79, 0x5b, 0xc4, 0xbd, 0xe1, 0x9b, 0xa2, 0xe1, 0x9a, 0xa3, 0xc5, - 0xb6, 0xc8, 0x99, 0xc2, 0xb3, 0xe1, 0x9b, 0x8d, 0xc3, 0xbd, 0xc3, 0x86, 0xc4, 0x89, - 0xe2, 0xb1, 0xb2, 0xe1, 0x9b, 0xb0, 0x73, 0xe1, 0x9b, 0x9a, 0x34, 0xc4, 0x8a, 0x56, - 0xc2, 0xb5, 0xc4, 0xba, 0xe2, 0xb1, 0xa7, 0xc7, 0xa4, 0xcd, 0xbe, 0xe2, 0xb1, 0xb6, - 0xc8, 0xbc, 0xc8, 0x81, 0xc7, 0xac, 0xc8, 0x8e, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x31, 0x1a, 0x81, 0xd9, 0x40, 0x52, 0x81, 0x50, 0xe5, 0xb4, 0x70, 0x97, 0x96, 0xb6, - 0x03, 0x22, 0x7a, 0xd1, 0xde, 0x1c, 0x15, 0x00, 0xef, 0x44, 0x5c, 0x40, 0xa4, 0xb2, - 0xaa, 0x21, 0xa1, 0x06, + 0xe1, 0x39, 0xe7, 0x7a, 0x0b, 0x76, 0xfc, 0x01, 0xa1, 0xec, 0x3b, 0x46, 0xf0, 0x21, + 0x29, 0x7b, 0xfb, 0x41, 0xd1, 0x7f, 0x5f, 0x98, 0x3d, 0xed, 0x61, 0x81, 0xee, 0xd9, + 0x5d, 0xe4, 0x2e, 0xb6, ], }, TestVector { key: [ - 0xff, 0x08, 0xec, 0x66, 0x3e, 0x18, 0x56, 0x51, 0x3f, 0xf6, 0x90, 0x80, 0x0c, 0x56, - 0x49, 0x91, 0xb2, 0x51, 0x67, 0xc2, 0xdd, 0x14, 0x07, 0x98, 0x13, 0x73, 0xc6, 0xd5, - 0x30, 0x65, 0xe3, 0x67, + 0x19, 0x58, 0x53, 0x8b, 0x12, 0x17, 0xa0, 0x3d, 0x89, 0xcd, 0x83, 0xb8, 0x3d, 0x0b, + 0xdd, 0x40, 0xa6, 0x9a, 0xbe, 0x3a, 0xc2, 0x5d, 0x00, 0xc6, 0xd2, 0x69, 0x97, 0xf9, + 0xf2, 0x57, 0x4d, 0x4f, ], description: [ - 0xe1, 0x9b, 0x9e, 0xe1, 0x9a, 0xba, 0xc5, 0x9e, 0xe1, 0x9b, 0xa0, 0xc5, 0xbd, 0xe2, - 0xb1, 0xb8, 0xc8, 0x99, 0xc6, 0xb6, 0xc5, 0x9b, 0xc3, 0x83, 0xc2, 0xbd, 0xe1, 0x9a, - 0xbb, 0xe1, 0x9b, 0xa3, 0xc7, 0x8b, 0xc5, 0xb1, 0xc4, 0x9e, 0xe1, 0x9b, 0xa5, 0xe2, - 0xb1, 0xa5, 0xc8, 0xac, 0x58, 0xcd, 0xb2, 0xc6, 0x88, 0xc6, 0x85, 0xc7, 0x90, 0xc6, - 0x81, 0xe2, 0xb1, 0xb7, 0xc3, 0xb7, 0xc8, 0xb8, 0x7b, 0xe1, 0x9a, 0xb8, 0x64, 0xc4, - 0x85, 0x5f, 0xcd, 0xb3, 0xc8, 0xb1, 0xc9, 0x87, 0xc4, 0xbf, 0xc5, 0xbe, 0x2f, 0xe1, - 0x9b, 0x8c, 0xc7, 0xbc, 0xc5, 0xab, 0xe1, 0x9a, 0xb6, 0xe2, 0xb1, 0xa9, 0xc6, 0xb7, - 0xc4, 0x89, 0x63, 0xc3, 0xa5, 0xc2, 0xbf, 0xc6, 0x9b, 0x5c, 0xc5, 0x93, 0xc6, 0xb0, - 0xc4, 0xaa, 0xc8, 0xb3, 0xc8, 0x89, 0xc7, 0xbc, 0xc2, 0xa7, 0xc7, 0xa9, 0xc8, 0xb2, - 0xc7, 0xbd, 0xe1, 0x9b, 0xa5, 0xc5, 0xb5, 0xc4, 0x8e, 0xc9, 0x82, 0xc5, 0x84, 0xc6, - 0x86, 0xc3, 0x8f, 0xc4, 0x85, 0xc6, 0xa0, 0xc8, 0xa3, 0xc6, 0xa6, 0xe1, 0x9a, 0xa9, - 0xc5, 0x99, 0xc2, 0xa8, 0xc7, 0xbc, 0xe2, 0xb1, 0xbb, 0xc4, 0xa4, 0xc6, 0xb9, 0xc7, - 0x90, 0xc9, 0x8c, 0xc3, 0x80, 0xc7, 0x8f, 0xc5, 0x94, 0xc8, 0x88, 0xc9, 0x86, 0xc3, - 0xbd, 0xe2, 0xb1, 0xb1, 0xe2, 0xb1, 0xbe, 0x3c, 0xc6, 0x87, 0x7c, 0x6f, 0xc3, 0xbd, - 0xc6, 0x94, 0xc2, 0xa1, 0x7d, 0xc3, 0x87, 0xc2, 0xac, 0xcd, 0xb3, 0xc8, 0xa3, 0xc2, - 0xbb, 0x61, 0xc4, 0x97, 0xc9, 0x8d, 0x6c, 0xe2, 0xb1, 0xad, 0xc5, 0x8c, 0xc5, 0xa3, - 0xc2, 0xb2, 0xe2, 0xb1, 0xbf, 0xc5, 0xa2, 0xe1, 0x9b, 0x85, 0xc6, 0xb0, 0x78, 0xe1, - 0x9b, 0x80, 0xcd, 0xb6, 0xc5, 0x80, 0xc8, 0x99, 0xc2, 0xba, 0xc3, 0x96, 0xc6, 0x8c, - 0xc7, 0xaf, 0xc4, 0x97, 0xc3, 0x8b, 0xc4, 0xa4, 0xc6, 0xb0, 0xc7, 0x98, 0xc4, 0x8d, - 0x62, 0xc8, 0x8b, 0xc6, 0xaf, 0x4d, 0xc8, 0xb7, 0xc8, 0x8c, 0x5e, 0xc2, 0xab, 0xc4, - 0x86, 0xc5, 0xb1, 0x2c, 0xe2, 0xb1, 0xad, 0xe1, 0x9b, 0xa7, 0xc9, 0x8a, 0xc3, 0xb1, - 0xc3, 0xae, 0xc4, 0x98, 0xc7, 0xbe, 0xc5, 0x94, 0xc3, 0x8a, 0xce, 0x89, 0xcd, 0xbb, - 0xc6, 0xbc, 0xcd, 0xb1, 0xc4, 0x8a, 0xc5, 0x90, 0xc6, 0xa9, 0xe1, 0x9b, 0x85, 0xc9, - 0x8c, 0x63, 0xe1, 0x9a, 0xab, 0x57, 0xc3, 0xb7, 0xc6, 0xb9, 0xce, 0x86, 0xc4, 0xa7, - 0xc3, 0x80, 0xc8, 0xb6, 0xe1, 0x9b, 0x9c, 0xcd, 0xb6, 0xc3, 0xa3, 0xc4, 0x9c, 0xe2, - 0xb1, 0xa7, 0xc7, 0x88, 0xc4, 0xb6, 0xc8, 0x80, 0xe1, 0x9b, 0x95, 0x33, 0xc2, 0xb6, - 0xc6, 0x86, 0xc6, 0xae, 0xc6, 0x97, 0xc7, 0x9b, 0xc7, 0xbf, 0xc6, 0x95, 0xe1, 0x9b, - 0xab, 0xc5, 0x8b, 0x58, 0xc7, 0x9a, 0xc8, 0x81, 0xc2, 0xb2, 0xc3, 0x8a, 0xc9, 0x81, - 0xc6, 0x84, 0xc6, 0xb5, 0xc5, 0xa6, 0xc5, 0xaf, 0xc9, 0x8e, 0xc2, 0xbc, 0xc7, 0xb5, - 0xe1, 0x9b, 0x8b, 0xc8, 0x8c, 0xc9, 0x89, 0xe1, 0x9b, 0x91, 0xe1, 0x9a, 0xb8, 0x3b, - 0xc5, 0x88, 0xc2, 0xa5, 0xc8, 0xa0, 0xc4, 0x97, 0xe1, 0x9b, 0x8d, 0xc2, 0xa4, 0xc3, - 0x83, 0xc3, 0x9a, 0x4c, 0xe1, 0x9b, 0x80, 0xc4, 0xa5, 0xc3, 0xa9, 0xc9, 0x80, 0xc7, - 0xb3, 0xc7, 0xb4, 0xe1, 0x9b, 0x80, 0xe1, 0x9b, 0x8e, 0xc5, 0xb9, 0xc2, 0xb9, 0xc6, - 0xb7, 0xc8, 0xbc, 0xc5, 0xa6, 0xc4, 0x83, 0xe1, 0x9b, 0x80, 0xc8, 0x92, 0xc4, 0x94, - 0xe1, 0x9b, 0x8c, 0xc8, 0xbc, 0xc4, 0x8e, 0xe2, 0xb1, 0xb3, 0x38, 0xc8, 0x8d, 0xc9, - 0x8c, 0xc6, 0xbd, 0xc3, 0x90, 0xc6, 0xa9, 0xc3, 0xb6, 0x66, 0x38, 0xc8, 0x97, 0xe1, - 0x9a, 0xa1, 0xc8, 0xb2, 0xc5, 0xab, 0xc7, 0x99, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x4f, 0x95, 0x9d, 0x88, 0x63, 0xb8, 0x36, 0x0b, 0x43, 0x30, 0x2b, 0xef, 0x35, 0x13, - 0x1f, 0x69, 0xd4, 0xf4, 0x26, 0xb1, 0x38, 0x3e, 0x54, 0xae, 0x38, 0x96, 0x40, 0x31, - 0xa8, 0xe5, 0xf9, 0x21, + 0xb3, 0xd1, 0x28, 0x2b, 0xf4, 0x32, 0x3d, 0xc3, 0x01, 0xa2, 0xd5, 0x9b, 0x8b, 0x80, + 0x41, 0xe2, 0xd2, 0x9b, 0x69, 0xee, 0x9b, 0xbc, 0x1c, 0x41, 0x06, 0xd3, 0x60, 0x1e, + 0x40, 0x39, 0x61, 0x3e, ], }, TestVector { key: [ - 0x39, 0xc4, 0x51, 0xf4, 0xd8, 0xdd, 0xcf, 0x69, 0x05, 0xed, 0xd8, 0x82, 0x5a, 0xd9, - 0x81, 0xb9, 0xe7, 0x3c, 0xa6, 0x83, 0x1c, 0xa2, 0xb3, 0xd7, 0xe8, 0xce, 0xf3, 0xd0, - 0xba, 0xaa, 0x31, 0x1b, + 0x9a, 0x0e, 0x46, 0x39, 0xb4, 0x69, 0x1f, 0x02, 0x7c, 0x0d, 0xb7, 0xfe, 0xf1, 0xbb, + 0x5e, 0xf9, 0x0a, 0xcd, 0xb7, 0x08, 0x62, 0x6d, 0x2e, 0x1f, 0x3e, 0x38, 0x3e, 0xe7, + 0x5b, 0x31, 0xcf, 0x57, ], description: [ - 0xc7, 0x94, 0xc5, 0xac, 0xc4, 0x97, 0xc6, 0xba, 0xe1, 0x9b, 0x97, 0xc7, 0x84, 0xc8, - 0xb6, 0xe1, 0x9b, 0x9d, 0xc8, 0x9c, 0xcd, 0xb5, 0xc3, 0x93, 0x7d, 0xc8, 0xa7, 0xc4, - 0x86, 0xc6, 0xab, 0xc4, 0x88, 0xe2, 0xb1, 0xb1, 0x6b, 0xe1, 0x9a, 0xa6, 0x68, 0xc6, - 0x9c, 0xc2, 0xb0, 0xc6, 0xba, 0xc7, 0xa6, 0xc5, 0x8c, 0xc9, 0x89, 0xc8, 0xbe, 0xc3, - 0xbc, 0xc5, 0xb7, 0xc7, 0x95, 0x5c, 0xc3, 0x96, 0xc4, 0x89, 0xc6, 0x84, 0xc3, 0xb2, - 0xc9, 0x87, 0x38, 0x45, 0xc3, 0xa1, 0xe2, 0xb1, 0xb1, 0x41, 0xc5, 0xac, 0xc6, 0xa1, - 0xc2, 0xa4, 0x34, 0xc4, 0xb0, 0xc2, 0xb5, 0xc5, 0xa2, 0xe1, 0x9a, 0xa9, 0xc4, 0xb6, - 0xc5, 0xbd, 0xc2, 0xbd, 0xc4, 0xa2, 0xc6, 0x8c, 0xc2, 0xb4, 0xc8, 0xb1, 0xc3, 0x95, - 0xc2, 0xbf, 0xc7, 0x99, 0xe1, 0x9b, 0x99, 0xc6, 0x8a, 0xc8, 0xaf, 0xc6, 0x9f, 0xc4, - 0x93, 0x38, 0xc7, 0x94, 0xc8, 0xa2, 0xc8, 0x84, 0xc6, 0x8f, 0x3a, 0xc5, 0xbd, 0xe1, - 0x9b, 0x97, 0xc5, 0x88, 0xcd, 0xb6, 0xc5, 0xa1, 0xc6, 0x96, 0xc4, 0x9f, 0xc3, 0x94, - 0xc5, 0xa6, 0xc4, 0xa4, 0xcd, 0xb2, 0xc7, 0xbc, 0xc8, 0x8d, 0x78, 0xc9, 0x86, 0xe2, - 0xb1, 0xab, 0xce, 0x84, 0xcd, 0xbd, 0xc4, 0x95, 0x65, 0xc7, 0x88, 0xc4, 0x84, 0xce, - 0x8c, 0xc3, 0xb1, 0xc2, 0xaf, 0xc6, 0xbf, 0xc4, 0x92, 0x25, 0xc7, 0xbc, 0xe2, 0xb1, - 0xb0, 0xc5, 0x93, 0xc7, 0x8a, 0xc5, 0x92, 0x75, 0xc4, 0xb7, 0xc6, 0x86, 0xc3, 0xbf, - 0xc7, 0xb7, 0xc6, 0xae, 0x67, 0xc2, 0xa9, 0xc6, 0x9e, 0xc5, 0x97, 0xcd, 0xb0, 0xc3, - 0x92, 0xc3, 0xac, 0xc5, 0xbb, 0xc3, 0x9a, 0xc3, 0xa1, 0xc6, 0x87, 0xc3, 0x8e, 0xc3, - 0xb8, 0xc9, 0x80, 0xe1, 0x9a, 0xa7, 0xc7, 0xac, 0x65, 0x24, 0xc6, 0x9c, 0xc3, 0xb2, - 0xc4, 0x82, 0x64, 0xc7, 0x8e, 0xc5, 0x8b, 0xe1, 0x9b, 0x8a, 0xe1, 0x9b, 0x89, 0xc9, - 0x82, 0xc3, 0x97, 0xc2, 0xac, 0xc4, 0x80, 0xc6, 0x83, 0xc4, 0xa6, 0xc4, 0xb6, 0xce, - 0x89, 0x52, 0x59, 0xce, 0x85, 0xc9, 0x85, 0xc6, 0xa9, 0xc4, 0x80, 0xc9, 0x8b, 0xe1, - 0x9a, 0xbe, 0xc9, 0x8e, 0xc2, 0xae, 0xc8, 0x96, 0xc6, 0x95, 0xc8, 0xaa, 0xe1, 0x9b, - 0x9b, 0xe1, 0x9a, 0xac, 0xc2, 0xb1, 0xc2, 0xaf, 0xc8, 0x8e, 0xe1, 0x9a, 0xae, 0xe1, - 0x9a, 0xa7, 0xc4, 0xb7, 0xc8, 0x82, 0xc3, 0xb2, 0xc4, 0xab, 0xc4, 0xbf, 0xc7, 0x91, - 0xe1, 0x9b, 0x9b, 0xc8, 0x90, 0xc7, 0x93, 0xe1, 0x9b, 0xa3, 0xc7, 0xb5, 0xc5, 0xa0, - 0xc8, 0xa2, 0xc8, 0x86, 0xc9, 0x86, 0xc2, 0xaa, 0x3d, 0xc4, 0xbf, 0xc5, 0xbc, 0xc4, - 0x96, 0xc3, 0xa5, 0xc3, 0x90, 0xc5, 0xb9, 0xc3, 0xa8, 0xc7, 0x80, 0xc5, 0x84, 0xce, - 0x89, 0xcd, 0xb7, 0xc3, 0x9b, 0xc2, 0xa1, 0xc7, 0x87, 0xcd, 0xb3, 0xe2, 0xb1, 0xb0, - 0xc8, 0x8b, 0xe1, 0x9a, 0xb1, 0xe1, 0x9a, 0xaf, 0x35, 0xc3, 0xa9, 0xc7, 0x85, 0xc4, - 0x85, 0xcd, 0xb1, 0xc4, 0xa7, 0xc3, 0x83, 0xe1, 0x9b, 0x94, 0xe2, 0xb1, 0xbf, 0xc4, - 0x80, 0x29, 0x2a, 0xc3, 0xa2, 0xe1, 0x9b, 0x88, 0xc3, 0x87, 0xe1, 0x9a, 0xb0, 0xc2, - 0xb8, 0xc7, 0x85, 0xc8, 0xb3, 0xc5, 0x84, 0x63, 0xc5, 0x8d, 0x3b, 0xc8, 0x9f, 0x5e, - 0xe2, 0xb1, 0xa5, 0x5c, 0xc4, 0x9f, 0xc8, 0xb4, 0xe2, 0xb1, 0xa0, 0xc7, 0xa5, 0x37, - 0xc6, 0xa2, 0xc8, 0xad, 0xe1, 0x9b, 0xa3, 0xc8, 0x96, 0xc6, 0xb6, 0xe2, 0xb1, 0xa4, - 0xc2, 0xb7, 0xc3, 0x86, 0xc4, 0xb1, 0xc6, 0x9f, 0xc8, 0x8c, 0xc8, 0xb3, 0xe1, 0x9a, - 0xa6, 0xc7, 0xa2, 0xc3, 0xbd, 0xe1, 0x9b, 0x8e, 0xc3, 0x9d, 0x21, 0xe1, 0x9b, 0xa5, - 0xe1, 0x9b, 0x8a, 0xc8, 0xb2, 0x24, 0xc8, 0x91, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x2c, 0x25, 0xd3, 0x35, 0xaf, 0xa2, 0xd5, 0x69, 0x72, 0x42, 0xf6, 0x18, 0x5c, 0x00, - 0x69, 0xd7, 0x9b, 0x33, 0xc8, 0x4a, 0x3f, 0x7f, 0x5c, 0x3c, 0x26, 0x5d, 0x3e, 0x88, - 0x45, 0xac, 0xcd, 0x0e, + 0x2b, 0xb6, 0x4e, 0xa4, 0x07, 0xfd, 0x00, 0xb6, 0x4c, 0xe5, 0x96, 0x2e, 0x57, 0x3f, + 0xaf, 0x9a, 0x40, 0xfb, 0x3a, 0x4d, 0xc1, 0xcc, 0xbf, 0x87, 0xe1, 0xe0, 0xe7, 0x97, + 0x8a, 0x19, 0xbd, 0x39, ], }, TestVector { key: [ - 0xa2, 0x8d, 0x36, 0xc0, 0xa0, 0xe1, 0xd6, 0x54, 0x57, 0x44, 0x64, 0xec, 0x5e, 0x0c, - 0x8d, 0x87, 0x85, 0x6e, 0xb9, 0xdf, 0xd2, 0x1d, 0x4d, 0xa2, 0xb9, 0xea, 0x34, 0xfd, - 0xe7, 0x3b, 0x19, 0x42, + 0x7d, 0xd6, 0xd7, 0x61, 0xe1, 0x02, 0x01, 0x37, 0xfa, 0x01, 0xb4, 0xdd, 0xd3, 0xb0, + 0xf3, 0x48, 0x04, 0xcc, 0x10, 0xcc, 0x4e, 0x9f, 0x6e, 0x9d, 0xf5, 0xb6, 0x04, 0x69, + 0xf5, 0x79, 0x36, 0x67, ], description: [ - 0xc8, 0x96, 0xcd, 0xba, 0xc7, 0x94, 0x32, 0xc5, 0xbf, 0xc7, 0x9c, 0xc3, 0x94, 0xc5, - 0x97, 0xc7, 0x84, 0xc2, 0xb1, 0xc3, 0xa9, 0x25, 0xc2, 0xb6, 0xc7, 0x84, 0xce, 0x84, - 0xc7, 0x8d, 0xc8, 0xb3, 0xe1, 0x9a, 0xa3, 0xc3, 0xb1, 0xe1, 0x9a, 0xa4, 0xc7, 0xa6, - 0xc2, 0xa6, 0xe1, 0x9a, 0xb5, 0xc8, 0x97, 0x3f, 0xc7, 0x90, 0x55, 0xc3, 0x97, 0xc5, - 0xb6, 0xc5, 0xa3, 0xc7, 0x98, 0xc7, 0xba, 0xc7, 0xa7, 0xc8, 0x89, 0xc3, 0x80, 0xe2, - 0xb1, 0xb8, 0xc7, 0x8c, 0xc6, 0xa2, 0xc5, 0xb2, 0xc6, 0x90, 0xc4, 0x8f, 0xc4, 0x84, - 0xc7, 0xa3, 0xc2, 0xbd, 0x48, 0xc3, 0x97, 0xc7, 0x8b, 0xc7, 0x85, 0xc5, 0x83, 0xc7, - 0xb9, 0xe1, 0x9b, 0x96, 0xc8, 0x90, 0xc3, 0xbd, 0xe1, 0x9b, 0x99, 0xc8, 0x9b, 0xcd, - 0xb5, 0x28, 0xe1, 0x9b, 0xaa, 0xc5, 0x9a, 0xc6, 0xb7, 0x39, 0x75, 0x25, 0xc7, 0xb8, - 0xc5, 0xaa, 0xc8, 0xa5, 0xc5, 0xb3, 0xc7, 0x96, 0xc3, 0x8c, 0xc5, 0x87, 0x72, 0xe1, - 0x9a, 0xab, 0x72, 0xc9, 0x8b, 0xc3, 0xb0, 0xc8, 0x96, 0xc3, 0xb2, 0xc3, 0x82, 0x52, - 0x78, 0x68, 0x4d, 0xc5, 0x9e, 0xc5, 0x80, 0xe1, 0x9b, 0x84, 0xe2, 0xb1, 0xa6, 0xc6, - 0x8d, 0x51, 0xc8, 0xbe, 0xe1, 0x9b, 0x8d, 0xc2, 0xbc, 0xc6, 0xa8, 0xc7, 0x93, 0xc8, - 0x90, 0xc5, 0x99, 0x28, 0x6e, 0xc8, 0x95, 0x78, 0xe1, 0x9b, 0x83, 0xc4, 0xbe, 0xc8, - 0xb5, 0xc5, 0x80, 0x67, 0xc3, 0x9b, 0xe1, 0x9b, 0x89, 0xc5, 0x8c, 0x75, 0xc4, 0xb7, - 0xe1, 0x9b, 0x87, 0x47, 0xc3, 0xa9, 0xc5, 0x95, 0xc4, 0x93, 0xc3, 0x9d, 0xe1, 0x9b, - 0x82, 0xc7, 0x95, 0xc5, 0x84, 0xc5, 0x91, 0xe1, 0x9a, 0xa2, 0xc3, 0x95, 0xcd, 0xb6, - 0xe2, 0xb1, 0xbd, 0xc5, 0x91, 0xc4, 0xbb, 0xc7, 0x8a, 0xc7, 0xaa, 0xc8, 0xb3, 0xe1, - 0x9b, 0xa6, 0x5d, 0xcd, 0xb2, 0xc5, 0xab, 0xc4, 0xa0, 0x5b, 0xc2, 0xbe, 0x37, 0x60, - 0xc5, 0x8a, 0xe1, 0x9a, 0xbb, 0xc9, 0x82, 0xc5, 0xbd, 0xe1, 0x9b, 0xa0, 0xc3, 0x80, - 0xc7, 0xb0, 0xc7, 0xa7, 0xe1, 0x9b, 0xaf, 0x70, 0xc5, 0x98, 0xc4, 0x80, 0x5b, 0xc2, - 0xb1, 0xe2, 0xb1, 0xab, 0xc3, 0x8b, 0xc4, 0xa3, 0x2f, 0xc6, 0x9a, 0xc5, 0x84, 0x63, - 0xc3, 0xaf, 0xc3, 0x87, 0xe1, 0x9b, 0xa0, 0xc3, 0x82, 0x73, 0xc4, 0x98, 0xe1, 0x9b, - 0x91, 0xe1, 0x9a, 0xac, 0x76, 0xc9, 0x87, 0xc7, 0xa8, 0xc4, 0x90, 0xc2, 0xb1, 0x2e, - 0x4b, 0xc8, 0x8b, 0x58, 0xc8, 0xb1, 0xe1, 0x9a, 0xbf, 0xc8, 0xa2, 0xc6, 0x9f, 0xc4, - 0xab, 0xc4, 0xbf, 0xe1, 0x9b, 0x86, 0xe1, 0x9b, 0xa8, 0xce, 0x8c, 0xc5, 0xab, 0xc3, - 0x85, 0x75, 0xc3, 0xa7, 0xc7, 0xbc, 0xc7, 0xa5, 0xe2, 0xb1, 0xba, 0xc5, 0xad, 0x6b, - 0xc8, 0xbc, 0xc6, 0xb0, 0xc5, 0xa0, 0xc4, 0x8f, 0xc7, 0xbd, 0xe1, 0x9a, 0xa2, 0xc5, - 0x90, 0xc6, 0xa2, 0xc9, 0x84, 0xc6, 0xab, 0xc7, 0xb2, 0x7c, 0xc5, 0x86, 0x40, 0x3b, - 0xc3, 0x8f, 0xe1, 0x9a, 0xb4, 0xe2, 0xb1, 0xae, 0xc2, 0xb0, 0xc5, 0x90, 0xe2, 0xb1, - 0xad, 0xc3, 0x85, 0xcd, 0xba, 0xc3, 0x9d, 0xe1, 0x9b, 0xab, 0xe1, 0x9a, 0xbd, 0xc6, - 0xb8, 0xc6, 0xab, 0xc3, 0xb0, 0xc9, 0x87, 0xc5, 0xb0, 0xc2, 0xaa, 0xc8, 0x99, 0xc6, - 0x80, 0xc9, 0x84, 0xc4, 0xb6, 0xe1, 0x9b, 0x82, 0xc6, 0xa9, 0xc5, 0xac, 0x76, 0xce, - 0x85, 0xc6, 0xa9, 0xe1, 0x9b, 0xa5, 0xc3, 0xac, 0xc4, 0xa0, 0xe1, 0x9a, 0xa7, 0xc2, - 0xae, 0xc3, 0xb2, 0xc3, 0x83, 0xc6, 0xaa, 0xc5, 0x88, 0xe1, 0x9a, 0xab, 0xc4, 0x81, - 0xc4, 0x98, 0xc4, 0xa3, 0x62, 0x7d, 0xc6, 0x84, 0xc8, 0x85, 0xc5, 0x96, 0xc4, 0xad, - 0xe2, 0xb1, 0xbd, 0x34, 0xc6, 0x9c, 0x5a, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x19, 0x8f, 0x36, 0xf8, 0x2b, 0xca, 0x02, 0x8c, 0x9f, 0x8d, 0x8d, 0x20, 0x9f, 0x4c, - 0x4a, 0xf6, 0xd1, 0x44, 0x90, 0x5e, 0xbb, 0x17, 0xda, 0xf4, 0x15, 0xd3, 0x1d, 0x4a, - 0xf3, 0x7c, 0x46, 0x2e, + 0xb0, 0x42, 0x08, 0x91, 0xec, 0xa6, 0x2d, 0x05, 0xd6, 0x89, 0x51, 0x5a, 0x33, 0x90, + 0xec, 0x57, 0xd8, 0xf7, 0xe9, 0x53, 0xea, 0x89, 0xcc, 0x01, 0xdd, 0xd3, 0xc1, 0x94, + 0x39, 0xe5, 0x18, 0x21, ], }, TestVector { key: [ - 0x28, 0x37, 0xcd, 0x45, 0x6e, 0x33, 0x19, 0x9f, 0x05, 0xb5, 0xd9, 0x0a, 0x42, 0xa7, - 0xb3, 0xd7, 0x13, 0xac, 0xd4, 0xec, 0xe6, 0xfe, 0x32, 0xdf, 0x77, 0xcb, 0x17, 0x00, - 0x43, 0xa2, 0xfa, 0xc6, + 0xbb, 0xf4, 0x49, 0x82, 0xf1, 0xba, 0x3a, 0x2b, 0x9d, 0xd3, 0xc1, 0x77, 0x4d, 0x71, + 0xce, 0x33, 0x60, 0x59, 0x9b, 0x07, 0xf2, 0x11, 0xc8, 0x16, 0xb8, 0xc4, 0x3b, 0x98, + 0x42, 0x23, 0x09, 0x24, ], description: [ - 0xc2, 0xb0, 0x5d, 0x32, 0xc3, 0x9a, 0xc3, 0x98, 0xc4, 0x88, 0xc5, 0x8f, 0xc4, 0xa4, - 0xc3, 0x81, 0xc7, 0x91, 0xe1, 0x9b, 0x9e, 0xc8, 0x9e, 0xc4, 0x86, 0xe1, 0x9b, 0xa6, - 0xc8, 0x89, 0xc8, 0x84, 0xce, 0x8c, 0xc9, 0x83, 0xe2, 0xb1, 0xac, 0xcd, 0xb2, 0xc3, - 0xb8, 0xc8, 0x81, 0x65, 0xc3, 0x82, 0xc3, 0x93, 0xc4, 0x81, 0x7e, 0xe1, 0x9a, 0xb0, - 0xc5, 0x82, 0xc9, 0x8f, 0xc4, 0xb9, 0xc3, 0xbe, 0xc8, 0x8d, 0xc7, 0xab, 0xce, 0x8c, - 0xe1, 0x9a, 0xb5, 0xc7, 0x8d, 0xc7, 0x87, 0xc7, 0xba, 0xc6, 0xa2, 0xc4, 0xa3, 0xc7, - 0x93, 0xc8, 0xa8, 0xc3, 0x90, 0xc2, 0xaa, 0x5f, 0xc7, 0xbb, 0xc3, 0xad, 0xe1, 0x9a, - 0xa1, 0xc8, 0xa7, 0xc3, 0xaa, 0x7e, 0xc3, 0xaa, 0xe2, 0xb1, 0xa0, 0xcd, 0xb1, 0xc4, - 0xa9, 0xc5, 0x98, 0x75, 0xc8, 0xa3, 0x5b, 0x2c, 0x44, 0xe2, 0xb1, 0xb4, 0xc6, 0x92, - 0xc3, 0x95, 0xc5, 0x8e, 0xc8, 0xa6, 0xc8, 0xb8, 0xc9, 0x82, 0xc7, 0x87, 0xc5, 0xa7, - 0xc8, 0x9f, 0x50, 0xc5, 0x9b, 0xc4, 0x9e, 0x56, 0x7c, 0xc5, 0xba, 0xc3, 0x85, 0xe1, - 0x9b, 0x8d, 0xe1, 0x9a, 0xbf, 0xc7, 0xaf, 0xc4, 0x89, 0xe1, 0x9b, 0x9e, 0xe1, 0x9b, - 0x95, 0xc8, 0xa8, 0xc6, 0xad, 0xe1, 0x9b, 0x80, 0xc7, 0x8d, 0x31, 0xc5, 0x83, 0xcd, - 0xb7, 0xc4, 0x9f, 0xc3, 0xa8, 0xc6, 0xa0, 0xe1, 0x9a, 0xa9, 0xc6, 0x8a, 0xc4, 0x9f, - 0x57, 0xc8, 0xaf, 0xc7, 0xa7, 0xe1, 0x9b, 0x9f, 0xc6, 0x88, 0xc8, 0xa4, 0xe2, 0xb1, - 0xbd, 0x3d, 0xc4, 0x83, 0xe1, 0x9b, 0xa1, 0x59, 0xe1, 0x9b, 0x94, 0x7c, 0x2b, 0xe1, - 0x9b, 0x86, 0xe1, 0x9a, 0xbb, 0xc7, 0xb7, 0xc9, 0x86, 0xc7, 0x9e, 0xc5, 0x82, 0xe2, - 0xb1, 0xb7, 0xc3, 0x98, 0xc5, 0xb9, 0xc3, 0xbe, 0xe1, 0x9b, 0x93, 0xc9, 0x87, 0xc7, - 0x92, 0xc9, 0x83, 0xc8, 0x8e, 0x30, 0xe1, 0x9b, 0x98, 0xc5, 0xba, 0xc6, 0x8a, 0x39, - 0x59, 0xc5, 0x96, 0xc4, 0x9f, 0xc7, 0x82, 0xe1, 0x9a, 0xb4, 0xc9, 0x8b, 0xc5, 0xbc, - 0xc3, 0xa6, 0xc8, 0x95, 0xc6, 0xa1, 0xc4, 0xba, 0xc6, 0xb0, 0xc5, 0xba, 0x7b, 0xc4, - 0x85, 0xc5, 0xb7, 0xc4, 0x85, 0xc7, 0x93, 0xc3, 0x8d, 0xc5, 0x85, 0xc7, 0xad, 0xc5, - 0x9e, 0xc4, 0xbc, 0xc2, 0xa4, 0xe1, 0x9b, 0x87, 0xc2, 0xab, 0xc8, 0xb1, 0xc6, 0x87, - 0x77, 0xc6, 0xb8, 0xc5, 0x85, 0xc4, 0x8c, 0xc7, 0xa3, 0xc3, 0x85, 0xc2, 0xb8, 0x42, - 0xc7, 0xa7, 0xce, 0x88, 0xe1, 0x9a, 0xaa, 0xe2, 0xb1, 0xa3, 0xe1, 0x9b, 0x9d, 0xe1, - 0x9b, 0xa6, 0xc6, 0xa4, 0xc7, 0x87, 0xc6, 0x81, 0xc4, 0x84, 0xc4, 0xae, 0xc9, 0x86, - 0xe2, 0xb1, 0xa8, 0xc2, 0xbc, 0xc4, 0x93, 0xe1, 0x9b, 0x88, 0xc7, 0x81, 0xc2, 0xa3, - 0xc4, 0xbf, 0xc7, 0x9d, 0xe1, 0x9b, 0x93, 0xe1, 0x9b, 0xa1, 0xe1, 0x9a, 0xb1, 0xc8, - 0xb3, 0xc2, 0xb9, 0xe1, 0x9b, 0x9c, 0x37, 0x64, 0xc3, 0xbf, 0xe2, 0xb1, 0xb0, 0x42, - 0xc8, 0xae, 0x7e, 0xc7, 0x92, 0xc4, 0xbd, 0xc6, 0x98, 0xc3, 0xab, 0xc3, 0x9d, 0xc4, - 0xa4, 0xc5, 0x99, 0xc5, 0x84, 0xc3, 0xba, 0xc7, 0xa0, 0xc6, 0x92, 0xc8, 0xad, 0xc5, - 0xb8, 0xc4, 0xb6, 0xc7, 0x90, 0xc6, 0xa3, 0xc3, 0xab, 0xc3, 0x95, 0xc4, 0x91, 0xce, - 0x84, 0x30, 0xc4, 0xb5, 0xc7, 0x9a, 0xc6, 0x92, 0xc2, 0xba, 0xc3, 0x9e, 0xc7, 0x99, - 0xc4, 0x8d, 0xc3, 0x85, 0xc6, 0x87, 0xc7, 0x86, 0xc6, 0x82, 0xc6, 0x8a, 0x26, 0xc4, - 0x8b, 0xc4, 0xbe, 0x7c, 0x58, 0xc8, 0xa6, 0xc4, 0xab, 0xc4, 0xbe, 0xce, 0x8a, 0xc4, - 0xb7, 0xc3, 0xb9, 0xc5, 0x94, 0xe1, 0x9b, 0x9a, 0xc8, 0xbf, 0xc7, 0xa6, 0xc5, 0xbd, - 0xc4, 0xa8, 0xc5, 0x8f, 0xc5, 0xa3, 0xcd, 0xb4, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x9a, 0xcc, 0xb2, 0xc5, 0xad, 0xae, 0x30, 0xab, 0xdb, 0x76, 0x58, 0x9d, 0xec, 0xdd, - 0x71, 0xe4, 0x65, 0x75, 0x53, 0x8d, 0x80, 0x3c, 0x28, 0xd7, 0xe4, 0xb8, 0x89, 0x6a, - 0xd7, 0xfd, 0xc6, 0x87, + 0xd7, 0x65, 0x7f, 0x6b, 0xf8, 0xc7, 0x11, 0x02, 0x3d, 0xbb, 0x0e, 0x3c, 0x6b, 0x7d, + 0x19, 0xae, 0xc5, 0x22, 0x4b, 0x5b, 0xc8, 0x33, 0x73, 0xe6, 0x89, 0x31, 0xbb, 0x3c, + 0x77, 0x8d, 0x19, 0x07, ], }, TestVector { key: [ - 0x0a, 0xbb, 0x5f, 0xa5, 0xab, 0x94, 0x0d, 0x29, 0x57, 0x4b, 0x95, 0x15, 0x9c, 0x40, - 0xbd, 0x79, 0x43, 0x44, 0xea, 0x07, 0xa8, 0x7e, 0x0f, 0x0f, 0x5f, 0xac, 0xa5, 0xb4, - 0x31, 0x81, 0x6d, 0xe2, + 0xb5, 0x9c, 0x5f, 0x32, 0x34, 0xd6, 0xca, 0x36, 0xcc, 0x48, 0x3d, 0x67, 0xa8, 0x4f, + 0x37, 0xd6, 0xb2, 0x4b, 0x24, 0x45, 0x48, 0x25, 0xd2, 0xb7, 0xbf, 0xdc, 0x80, 0x2b, + 0x2e, 0x32, 0x8c, 0x43, ], description: [ - 0xcd, 0xbd, 0xe1, 0x9a, 0xb4, 0xc8, 0x9c, 0xc8, 0x96, 0xc6, 0xaa, 0xce, 0x88, 0xe1, - 0x9a, 0xb4, 0x2d, 0xc6, 0x81, 0xc7, 0x86, 0xc7, 0x9d, 0xc5, 0x86, 0xc4, 0x84, 0xe1, - 0x9b, 0x9e, 0xc6, 0x91, 0xe1, 0x9b, 0x89, 0x2d, 0xc4, 0x81, 0xc6, 0x88, 0x4c, 0xc2, - 0xbf, 0xc2, 0xaa, 0xc3, 0xb4, 0xc8, 0xae, 0xc7, 0x91, 0xc2, 0xb0, 0xc5, 0xa5, 0xc6, - 0xa3, 0xc4, 0x94, 0x38, 0xe1, 0x9a, 0xa9, 0xc4, 0xbe, 0xc3, 0x81, 0xe2, 0xb1, 0xba, - 0xc5, 0xa7, 0x44, 0xc3, 0xbb, 0xc8, 0x97, 0x5b, 0xc6, 0xbd, 0xc5, 0x9b, 0xe2, 0xb1, - 0xa0, 0xe1, 0x9a, 0xbd, 0xc2, 0xbf, 0xc3, 0x94, 0xc2, 0xb0, 0xc4, 0x9c, 0xc6, 0xa1, - 0xc3, 0xb6, 0xc2, 0xac, 0xe1, 0x9a, 0xa9, 0xc6, 0x90, 0x5c, 0xe2, 0xb1, 0xb2, 0x7c, - 0xc3, 0x86, 0x57, 0xe1, 0x9a, 0xa4, 0x71, 0xc7, 0x91, 0xe2, 0xb1, 0xbc, 0xc7, 0xab, - 0xc4, 0x86, 0xc5, 0xa1, 0xc5, 0x92, 0xc6, 0xa9, 0x78, 0x7a, 0xcd, 0xbd, 0xc4, 0xb2, - 0x75, 0xc8, 0xa4, 0x29, 0xc6, 0x9e, 0xc7, 0xa0, 0xc7, 0xa8, 0xc3, 0xa5, 0xc4, 0xb6, - 0xe1, 0x9b, 0x9d, 0x45, 0xc3, 0xa1, 0xc9, 0x8b, 0xc3, 0xbb, 0xc8, 0x86, 0xc7, 0x83, - 0xe1, 0x9b, 0x93, 0xc8, 0x9c, 0xc5, 0xba, 0xc7, 0xa8, 0xc7, 0x8d, 0xe1, 0x9a, 0xbf, - 0xcd, 0xb7, 0xc5, 0x91, 0xc4, 0x8f, 0x65, 0xc3, 0xa4, 0xc2, 0xb4, 0xe2, 0xb1, 0xb0, - 0xc8, 0xa9, 0xc8, 0xb1, 0xe1, 0x9b, 0x86, 0xc8, 0x85, 0xc4, 0x8d, 0xe1, 0x9a, 0xaf, - 0xe1, 0x9b, 0x81, 0xc3, 0xae, 0xc4, 0x90, 0xc8, 0xbb, 0x2a, 0xe1, 0x9a, 0xae, 0xc6, - 0xa3, 0xc4, 0xae, 0xc4, 0x93, 0x7b, 0xc7, 0x8e, 0xc8, 0x9c, 0xc3, 0xa3, 0xc5, 0xac, - 0xcd, 0xb7, 0x41, 0xc5, 0xb5, 0xc4, 0x95, 0xc3, 0x92, 0xc6, 0xaa, 0xe1, 0x9b, 0x83, - 0xe2, 0xb1, 0xb2, 0xe1, 0x9a, 0xbc, 0xc7, 0x87, 0xc7, 0xb7, 0xc5, 0x97, 0xc2, 0xb9, - 0x55, 0xe1, 0x9b, 0x91, 0x45, 0xc8, 0x8b, 0xe1, 0x9a, 0xa9, 0xe1, 0x9a, 0xad, 0xc6, - 0xab, 0xcd, 0xbd, 0xc8, 0xa2, 0xc3, 0x8c, 0x23, 0xe2, 0xb1, 0xa9, 0xe1, 0x9b, 0x8c, - 0xe1, 0x9a, 0xb7, 0xc6, 0x84, 0xc3, 0xa6, 0xc8, 0xb2, 0xc5, 0xbe, 0xc7, 0xbe, 0xe1, - 0x9a, 0xaa, 0xc3, 0xb1, 0xc6, 0xb3, 0xe1, 0x9a, 0xbe, 0xc4, 0xa0, 0xc2, 0xb9, 0x37, - 0xc5, 0x82, 0xc5, 0xbf, 0xc8, 0xa9, 0x44, 0xe1, 0x9b, 0x8d, 0xe1, 0x9b, 0x9c, 0xe1, - 0x9b, 0xa6, 0xe1, 0x9b, 0x87, 0xe2, 0xb1, 0xaa, 0xc7, 0xab, 0xc7, 0xaf, 0xc7, 0x91, - 0xe2, 0xb1, 0xbf, 0xe1, 0x9a, 0xb9, 0xe1, 0x9b, 0x87, 0xc6, 0xb6, 0xc5, 0xa3, 0xc7, - 0x98, 0xc5, 0x90, 0xc8, 0x9b, 0xcd, 0xb2, 0xc4, 0x97, 0x7b, 0xe1, 0x9b, 0x96, 0xc3, - 0xa2, 0x6e, 0xc8, 0x90, 0x6f, 0xc6, 0x9e, 0xc9, 0x87, 0x6e, 0xc8, 0x82, 0xc4, 0x97, - 0xc3, 0xb0, 0xc4, 0x8d, 0xe2, 0xb1, 0xb6, 0xc7, 0x9b, 0xc3, 0x9a, 0xc8, 0xb5, 0xc6, - 0x95, 0xc3, 0xb6, 0x44, 0xc3, 0xac, 0xc5, 0x90, 0xe1, 0x9b, 0xa0, 0xc8, 0x92, 0x5d, - 0xc5, 0xad, 0xe1, 0x9b, 0xab, 0x7b, 0xc4, 0x97, 0xc7, 0xac, 0xe1, 0x9a, 0xb2, 0x2e, - 0xc3, 0xa8, 0xc6, 0xbc, 0x40, 0x23, 0xc6, 0xbf, 0x55, 0xc6, 0x82, 0xc7, 0x8a, 0x65, - 0xe1, 0x9b, 0x9b, 0xc3, 0xbc, 0xe2, 0xb1, 0xbd, 0x4b, 0xe2, 0xb1, 0xa7, 0xc6, 0x8d, - 0xc7, 0xae, 0xc3, 0x83, 0xc7, 0xb9, 0x64, 0xc6, 0xa6, 0xc8, 0x82, 0xc8, 0x9d, 0xc8, - 0x9f, 0xc8, 0xb2, 0xc6, 0x8c, 0xe2, 0xb1, 0xb8, 0xe1, 0x9b, 0xab, 0xc7, 0x9d, 0xc8, - 0xb5, 0xc5, 0xab, 0xcd, 0xba, 0x45, 0xe1, 0x9a, 0xab, 0xc3, 0x93, 0x37, 0xc2, 0xb8, - 0xc9, 0x8d, 0xc9, 0x88, 0xe1, 0x9b, 0x93, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x1a, 0x28, 0x12, 0x59, 0x8b, 0xa7, 0x5c, 0x9a, 0x69, 0x2e, 0x69, 0x19, 0x65, 0x7f, - 0x68, 0x3e, 0xa4, 0x52, 0xe1, 0xae, 0x66, 0x33, 0xba, 0xcf, 0xe1, 0xf4, 0x54, 0x55, - 0x04, 0xc2, 0x95, 0xb4, + 0x75, 0xb8, 0x7f, 0x88, 0x85, 0xb9, 0x7f, 0x00, 0x70, 0x93, 0x89, 0xeb, 0x57, 0x0a, + 0x59, 0x47, 0xd4, 0xca, 0x36, 0x19, 0x98, 0x10, 0xbd, 0x94, 0x37, 0xba, 0x7c, 0x4a, + 0x2b, 0x24, 0xcd, 0xab, ], }, TestVector { key: [ - 0x07, 0x82, 0x6a, 0x86, 0xb1, 0x72, 0xc5, 0x5a, 0x66, 0x40, 0x1e, 0x37, 0xfc, 0x69, - 0xf9, 0x2e, 0x6d, 0x62, 0xa0, 0x14, 0x6c, 0x67, 0x06, 0x29, 0xa2, 0xd6, 0x51, 0x34, - 0xdf, 0x3b, 0x22, 0xf6, + 0xff, 0x63, 0xc7, 0x89, 0x25, 0x1c, 0x10, 0x43, 0xc6, 0xf9, 0x6c, 0x66, 0xbf, 0x5b, + 0x0f, 0x61, 0xc9, 0xd6, 0x5f, 0xef, 0x5a, 0xaf, 0x42, 0x84, 0xa6, 0xa5, 0x69, 0x94, + 0x94, 0x1c, 0x05, 0xfa, ], description: [ - 0xc6, 0xbe, 0xc3, 0x90, 0xc3, 0x92, 0xc4, 0x97, 0x7e, 0xc6, 0x81, 0x34, 0xe1, 0x9b, - 0x86, 0xc8, 0x8e, 0x7e, 0xcd, 0xb4, 0xc6, 0x90, 0x7b, 0xc4, 0xae, 0xc7, 0x8a, 0xcd, - 0xbd, 0xc5, 0x98, 0xc5, 0xa2, 0xc2, 0xb7, 0x6a, 0xc5, 0x82, 0xc5, 0xac, 0xc2, 0xbf, - 0xc7, 0x83, 0x33, 0xe1, 0x9b, 0x87, 0xc5, 0xb8, 0xc8, 0xb7, 0xc5, 0xaa, 0xc5, 0xb1, - 0xc8, 0xbd, 0x47, 0x58, 0xc5, 0x90, 0xc6, 0x91, 0xc5, 0x89, 0x24, 0xc4, 0xbf, 0xc4, - 0x80, 0xc3, 0xa7, 0xc8, 0xaa, 0x69, 0x6e, 0xe1, 0x9b, 0xa0, 0xc2, 0xa1, 0x42, 0xc6, - 0x96, 0xc3, 0x9b, 0x76, 0x24, 0xcd, 0xbb, 0xc6, 0xaf, 0x52, 0x24, 0xc4, 0xa0, 0xe1, - 0x9b, 0x82, 0xc5, 0x82, 0xe1, 0x9b, 0x90, 0xc8, 0x92, 0xc5, 0xb1, 0xe2, 0xb1, 0xbd, - 0xc2, 0xb7, 0xc4, 0xa6, 0xce, 0x88, 0xe1, 0x9b, 0x91, 0x3e, 0x3b, 0xc8, 0x88, 0xc6, - 0xae, 0xe2, 0xb1, 0xa4, 0xc3, 0xa1, 0xc4, 0x84, 0xe1, 0x9a, 0xbd, 0xc3, 0x9c, 0xc8, - 0xa4, 0xe1, 0x9a, 0xba, 0xc7, 0xb1, 0xc2, 0xac, 0x6f, 0xc4, 0x87, 0xc8, 0xb9, 0xe1, - 0x9a, 0xae, 0xc2, 0xab, 0xc3, 0xad, 0xc3, 0xae, 0xc5, 0x8b, 0xc8, 0x9f, 0xce, 0x8c, - 0x4f, 0xc7, 0xbe, 0xc7, 0x8a, 0xc5, 0xbe, 0xc7, 0x92, 0xe1, 0x9b, 0x9d, 0xc5, 0x95, - 0xe1, 0x9a, 0xab, 0x57, 0xe1, 0x9a, 0xb0, 0xc4, 0x9e, 0x55, 0xc6, 0x96, 0xc4, 0xb2, - 0x29, 0xc9, 0x81, 0xc4, 0xa6, 0xc7, 0xa3, 0xc4, 0x91, 0xc6, 0x96, 0xc7, 0x80, 0xc7, - 0xb6, 0xe1, 0x9b, 0x8c, 0xc3, 0x9f, 0xc5, 0x92, 0xc8, 0x90, 0x72, 0xc6, 0x99, 0xe1, - 0x9a, 0xa3, 0x7e, 0xc3, 0xa6, 0x58, 0xc8, 0xb6, 0x6c, 0xc8, 0xb0, 0xc3, 0xbb, 0xe1, - 0x9a, 0xa4, 0xc2, 0xa5, 0xc3, 0x94, 0xcd, 0xb4, 0xc3, 0xb7, 0xc6, 0xa7, 0xc2, 0xaf, - 0xc4, 0xae, 0xc3, 0xa5, 0xe2, 0xb1, 0xb7, 0xe1, 0x9b, 0x86, 0x5d, 0xc5, 0x80, 0x3f, - 0xc6, 0x92, 0xc8, 0x8e, 0xc6, 0xb4, 0xc7, 0xac, 0xc6, 0xb4, 0xc3, 0xab, 0xc9, 0x8b, - 0xc4, 0x90, 0xe2, 0xb1, 0xa7, 0xc4, 0x96, 0xc4, 0x9a, 0xc8, 0xbd, 0xce, 0x87, 0xc6, - 0xaf, 0xc7, 0x83, 0x3c, 0xe1, 0x9b, 0xa2, 0xc2, 0xb4, 0xc4, 0x9d, 0x4d, 0xc3, 0x8a, - 0xc3, 0xa8, 0xc6, 0x93, 0x28, 0xc7, 0xbc, 0xc6, 0x9f, 0xe1, 0x9a, 0xa4, 0xc7, 0x8a, - 0xc7, 0xbd, 0xc3, 0x87, 0xe2, 0xb1, 0xb7, 0xc6, 0xac, 0xe1, 0x9b, 0x92, 0xc4, 0xa6, - 0xc5, 0x84, 0x3a, 0xc9, 0x8c, 0xc9, 0x8a, 0xc2, 0xa2, 0xc4, 0xae, 0xe1, 0x9a, 0xa8, - 0xc8, 0xa6, 0xc2, 0xbb, 0xe2, 0xb1, 0xa0, 0xc8, 0xab, 0xc7, 0x8a, 0xc7, 0x8a, 0xe1, - 0x9b, 0x97, 0xc4, 0x89, 0xc8, 0x9a, 0xe1, 0x9b, 0x91, 0xe1, 0x9b, 0x8b, 0x5c, 0xe1, - 0x9b, 0xa6, 0xc8, 0xa9, 0xc9, 0x80, 0x71, 0xc5, 0x95, 0xc8, 0xbd, 0xc5, 0x8f, 0xc5, - 0xa4, 0xc3, 0xb7, 0xc6, 0x85, 0xe1, 0x9a, 0xa9, 0xc6, 0xa2, 0xc8, 0xaa, 0xc7, 0x8c, - 0xc6, 0x9a, 0xc7, 0x81, 0xc3, 0xa0, 0xc4, 0xab, 0xc8, 0xa2, 0xc5, 0x9b, 0xc5, 0xbb, - 0xc7, 0x8e, 0xc6, 0x95, 0xc8, 0x9c, 0x6c, 0xc8, 0xbc, 0xe2, 0xb1, 0xaa, 0xc5, 0x85, - 0xc6, 0x83, 0xc4, 0xa1, 0xc4, 0x81, 0x4d, 0xe1, 0x9a, 0xa3, 0x5d, 0xc3, 0xb7, 0xc5, - 0xa4, 0xc6, 0xae, 0xe1, 0x9a, 0xad, 0xc4, 0xa4, 0xc3, 0xb8, 0xc3, 0xa7, 0x44, 0xc6, - 0xaa, 0xc7, 0xa5, 0xc4, 0xa4, 0xc5, 0xba, 0xc9, 0x85, 0xc3, 0xa6, 0xc3, 0x9d, 0xc4, - 0xa3, 0xc5, 0x97, 0xe1, 0x9a, 0xa1, 0x2d, 0x77, 0xc2, 0xa2, 0x3e, 0xc8, 0x8d, 0xc5, - 0xb1, 0xe1, 0x9a, 0xa9, 0xc6, 0xa2, 0xc5, 0xbf, 0xc7, 0xa4, 0xe1, 0x9a, 0xa9, 0xc6, - 0x82, 0xc3, 0xbb, 0xc3, 0xb9, 0xc6, 0x8a, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x9f, 0x0a, 0x47, 0x16, 0x90, 0xe2, 0x1a, 0x74, 0x79, 0x7a, 0x71, 0x12, 0x36, 0xfa, - 0x58, 0x85, 0x5e, 0x6e, 0xd1, 0xaa, 0xd5, 0x4c, 0x6c, 0xaf, 0xa8, 0xa3, 0x7f, 0xdb, - 0xd1, 0x9e, 0x75, 0x00, + 0xda, 0x94, 0x71, 0x63, 0xc0, 0x67, 0x83, 0x60, 0xfc, 0x58, 0xb4, 0x33, 0xa8, 0x97, + 0x66, 0x8e, 0x58, 0x2e, 0xce, 0x67, 0x8f, 0x31, 0xac, 0xb4, 0xb4, 0xad, 0x85, 0xbc, + 0xd5, 0xf7, 0xc1, 0xae, ], }, TestVector { key: [ - 0xe5, 0x21, 0x62, 0x0d, 0x68, 0x53, 0x7c, 0xa3, 0x0b, 0xf3, 0x2f, 0x4e, 0xd2, 0xc3, - 0x3c, 0x0c, 0xf8, 0x25, 0x2a, 0x79, 0xa8, 0xa7, 0x39, 0x7b, 0xf1, 0x1b, 0x67, 0x87, - 0x30, 0x52, 0x44, 0xee, + 0x45, 0x61, 0x9f, 0x20, 0x6c, 0x3b, 0xfc, 0x84, 0xfd, 0x42, 0x4f, 0xfb, 0x5c, 0x81, + 0x6f, 0x65, 0x4b, 0x27, 0xaa, 0x7f, 0x7b, 0x4b, 0xd6, 0x7e, 0xc5, 0xf9, 0xac, 0x6d, + 0x0f, 0x38, 0xdb, 0xb1, ], description: [ - 0xcd, 0xb4, 0xc6, 0x92, 0xc4, 0xa7, 0xc3, 0xb9, 0xc6, 0xb8, 0xcd, 0xba, 0xc4, 0xa4, - 0xc8, 0x9a, 0xc8, 0xb7, 0xe1, 0x9b, 0x92, 0xc5, 0xb2, 0xc7, 0xba, 0xc6, 0x86, 0xc8, - 0x87, 0xc5, 0x88, 0xc5, 0x87, 0xc7, 0xa0, 0xc8, 0x97, 0xc6, 0xad, 0x4e, 0xc4, 0xa4, - 0xc5, 0x9c, 0xc8, 0x89, 0xc5, 0xba, 0xc3, 0xa1, 0xc8, 0xa2, 0xc3, 0x9c, 0xc8, 0x88, - 0xe1, 0x9a, 0xa1, 0xc8, 0x9d, 0xc7, 0x8b, 0xc6, 0xb5, 0xc3, 0x87, 0xc3, 0x93, 0xc4, - 0xa3, 0xe2, 0xb1, 0xa2, 0x7b, 0xc9, 0x85, 0xc3, 0xb9, 0x3a, 0xc6, 0x86, 0xc3, 0x99, - 0xc7, 0xa2, 0xc7, 0x83, 0xce, 0x8a, 0xc7, 0xbd, 0xe1, 0x9b, 0xa7, 0xc8, 0xa9, 0xe2, - 0xb1, 0xb1, 0xc6, 0x9d, 0xc5, 0xbb, 0xc7, 0x98, 0xc6, 0x8a, 0xc6, 0x8d, 0xc5, 0xa2, - 0xc9, 0x89, 0xc3, 0xa3, 0x57, 0xc8, 0xa8, 0x66, 0xc7, 0x95, 0xc6, 0x86, 0xc4, 0xa2, - 0x79, 0xc8, 0x98, 0xc7, 0xa8, 0xc7, 0x86, 0xc6, 0xb4, 0xcd, 0xb6, 0xe1, 0x9a, 0xb2, - 0xc3, 0x9e, 0xc2, 0xb1, 0xc7, 0xbb, 0xc6, 0xa2, 0xc7, 0xb2, 0xc5, 0xa6, 0x48, 0xc2, - 0xbf, 0xc3, 0xac, 0x53, 0xc4, 0x80, 0xc2, 0xba, 0xc2, 0xbc, 0xc7, 0xad, 0xc7, 0xa7, - 0xc6, 0xb3, 0xe1, 0x9a, 0xad, 0xc8, 0x90, 0xe2, 0xb1, 0xaa, 0x31, 0xc5, 0xb9, 0xc5, - 0xbe, 0xe1, 0x9a, 0xb9, 0xc2, 0xb7, 0xc6, 0x91, 0xc3, 0xad, 0x7c, 0xc3, 0xa6, 0xc3, - 0x95, 0xc5, 0x8c, 0xc3, 0xb9, 0xc6, 0xa7, 0xc4, 0x9e, 0x7d, 0xce, 0x8c, 0x2e, 0xc7, - 0x97, 0xc6, 0x9b, 0x26, 0xc6, 0xa8, 0x7c, 0xc7, 0xa2, 0x4d, 0xc8, 0x9d, 0xc5, 0x8c, - 0xe2, 0xb1, 0xa5, 0xc7, 0xae, 0xc4, 0xbe, 0xe1, 0x9b, 0xa6, 0xc2, 0xae, 0xe2, 0xb1, - 0xaf, 0xc5, 0xb9, 0xc7, 0x87, 0x66, 0x5c, 0x53, 0xe2, 0xb1, 0xb1, 0xc8, 0xbf, 0xc5, - 0x8a, 0xc3, 0xa8, 0x21, 0xc3, 0x8c, 0xc4, 0x99, 0xc3, 0xba, 0xcd, 0xbe, 0xc8, 0xa6, - 0xc3, 0xbe, 0xc8, 0x90, 0xc4, 0x9d, 0xc4, 0x94, 0xc3, 0xbf, 0xc3, 0x9a, 0xe1, 0x9b, - 0x83, 0xc4, 0xb9, 0xc2, 0xa6, 0xc8, 0x8d, 0xc6, 0x82, 0xe1, 0x9a, 0xb0, 0xc6, 0x9b, - 0x31, 0xe1, 0x9b, 0x97, 0xc6, 0x91, 0xc3, 0x83, 0xc6, 0x9c, 0x4d, 0xc3, 0xb7, 0xc7, - 0x99, 0xe1, 0x9a, 0xac, 0xc2, 0xa3, 0xc6, 0xad, 0xc4, 0xb4, 0xc3, 0xa6, 0xe2, 0xb1, - 0xb2, 0x26, 0xc4, 0x84, 0xc7, 0x92, 0xc8, 0x8d, 0xc5, 0x93, 0x30, 0xc4, 0x94, 0xc5, - 0x88, 0xc5, 0xa1, 0xc2, 0xa1, 0xc7, 0x9f, 0xc4, 0xbb, 0xc7, 0x99, 0xc6, 0xac, 0xc6, - 0xa7, 0xc9, 0x87, 0x2b, 0xc6, 0x99, 0xce, 0x85, 0xe1, 0x9b, 0x9d, 0xc3, 0x90, 0xe1, - 0x9a, 0xad, 0xe1, 0x9b, 0x94, 0xc3, 0x97, 0xc4, 0x84, 0xc4, 0x9c, 0xc7, 0x84, 0xc3, - 0x8f, 0x79, 0xc5, 0xa1, 0xc8, 0x98, 0xc5, 0xa9, 0x64, 0xc3, 0xa9, 0x4d, 0xc4, 0x9c, - 0xc6, 0xba, 0xe2, 0xb1, 0xb8, 0xc5, 0xbe, 0xe2, 0xb1, 0xbf, 0xc3, 0x88, 0xc5, 0x81, - 0xc3, 0xbc, 0xc9, 0x84, 0xc6, 0xbb, 0xc6, 0xa3, 0xe1, 0x9b, 0x8c, 0x34, 0xc3, 0x85, - 0x78, 0xc8, 0xb8, 0xc2, 0xa4, 0xc5, 0xb2, 0xc6, 0x82, 0x40, 0xc6, 0xa3, 0xc3, 0xb0, - 0xc6, 0xb3, 0x72, 0xc2, 0xa1, 0xc6, 0x92, 0xc7, 0xa2, 0xe2, 0xb1, 0xa5, 0xc9, 0x8a, - 0xe2, 0xb1, 0xbb, 0xc2, 0xb5, 0xe2, 0xb1, 0xb0, 0xc4, 0x88, 0xc3, 0x95, 0xc6, 0xb2, - 0xc7, 0x9d, 0xc7, 0x96, 0xe2, 0xb1, 0xaa, 0x67, 0xc3, 0xa3, 0xc4, 0xbf, 0xc9, 0x8b, - 0xc4, 0xb9, 0xc4, 0xa8, 0xe2, 0xb1, 0xad, 0x41, 0xc3, 0xb4, 0xce, 0x86, 0xc4, 0xbe, - 0xc6, 0xa8, 0xe2, 0xb1, 0xa4, 0xc5, 0xbc, 0xc8, 0x94, 0xe1, 0x9b, 0x8b, 0xc5, 0x81, - 0xe1, 0x9b, 0x94, 0xc4, 0xae, 0xc5, 0x91, 0x5a, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x6e, 0xa8, 0x83, 0xd2, 0x4d, 0x3d, 0xbc, 0xf6, 0xc7, 0xad, 0x2b, 0xf7, 0xed, 0x87, - 0xc7, 0xb5, 0xaa, 0xd2, 0x13, 0xf0, 0xcf, 0x11, 0x2b, 0xff, 0xa0, 0xc8, 0x84, 0xb1, - 0xd0, 0xe0, 0x56, 0x9a, + 0x01, 0xcb, 0x2c, 0x3f, 0xae, 0x16, 0xf2, 0xe5, 0x82, 0x1e, 0x34, 0x64, 0x70, 0x17, + 0xf3, 0x68, 0x04, 0xb1, 0x7e, 0xfc, 0x8c, 0xb7, 0x69, 0x54, 0x79, 0x61, 0x5d, 0xe8, + 0xf3, 0x2d, 0x29, 0x21, ], }, TestVector { key: [ - 0x5e, 0x09, 0x49, 0x46, 0x12, 0x04, 0x4d, 0x6e, 0xf5, 0xb0, 0xbd, 0x0d, 0xc9, 0xb4, - 0xa0, 0xdc, 0x09, 0x47, 0x76, 0x00, 0x9e, 0x1e, 0x94, 0xce, 0x39, 0x1c, 0x27, 0xb8, - 0xb7, 0x51, 0x8f, 0xe0, + 0xbf, 0x39, 0x20, 0xce, 0x2e, 0x9e, 0x95, 0xb0, 0xee, 0xce, 0x13, 0x0a, 0x50, 0xba, + 0x7d, 0xcc, 0x6f, 0x26, 0x51, 0x2a, 0x9f, 0xc7, 0xb8, 0x04, 0xaf, 0xf0, 0x89, 0xf5, + 0x0c, 0xbc, 0xff, 0xf7, ], description: [ - 0xc7, 0x9b, 0xc7, 0xa3, 0xc3, 0xa4, 0xc4, 0xa7, 0xe1, 0x9a, 0xa4, 0x29, 0xc9, 0x88, - 0xc3, 0xa8, 0xe1, 0x9b, 0x9c, 0x71, 0xc5, 0xaa, 0xe2, 0xb1, 0xba, 0xc8, 0xac, 0xc7, - 0xa5, 0xc8, 0x8b, 0xcd, 0xbc, 0xe1, 0x9b, 0x83, 0xc9, 0x86, 0xe1, 0x9a, 0xad, 0xc6, - 0x87, 0xc8, 0xbc, 0xc8, 0xa0, 0xc3, 0x94, 0xe1, 0x9b, 0xa5, 0xc8, 0x94, 0xc3, 0xac, - 0xc3, 0x88, 0xe1, 0x9a, 0xa8, 0xc7, 0xb9, 0x5b, 0xe2, 0xb1, 0xa0, 0xe1, 0x9b, 0x9f, - 0x63, 0xc6, 0x95, 0xcd, 0xb3, 0xc3, 0x8d, 0xc6, 0x88, 0xc2, 0xaf, 0xc4, 0x93, 0x64, - 0xc6, 0x89, 0xc8, 0xa0, 0x5b, 0xc8, 0x8b, 0xc6, 0xb7, 0xe1, 0x9b, 0xa0, 0x4c, 0x31, - 0x31, 0xc3, 0x82, 0xc7, 0xa5, 0xc9, 0x8e, 0xc9, 0x8f, 0xe1, 0x9b, 0x93, 0xc7, 0x80, - 0xc4, 0x80, 0xc3, 0xb2, 0xc3, 0x88, 0xe2, 0xb1, 0xb9, 0xc8, 0xab, 0xcd, 0xbb, 0xc3, - 0x8c, 0xc9, 0x87, 0xe1, 0x9a, 0xaf, 0xc8, 0xa9, 0xc9, 0x84, 0xc4, 0x8b, 0xe1, 0x9b, - 0x90, 0xc7, 0xb2, 0xc5, 0xaa, 0xc6, 0x9d, 0xc8, 0xb3, 0xe1, 0x9b, 0x9f, 0xc2, 0xa6, - 0xc5, 0xba, 0xe1, 0x9b, 0xa4, 0xc4, 0x8a, 0x4f, 0xc3, 0x85, 0xc5, 0x9d, 0x5f, 0xc8, - 0xbf, 0x6d, 0x41, 0xc7, 0x8b, 0xe1, 0x9a, 0xb3, 0xc7, 0xb3, 0xc4, 0x89, 0xc6, 0xaf, - 0xc6, 0xb7, 0xc3, 0x8f, 0xc5, 0x9d, 0xc8, 0x82, 0xc8, 0x82, 0xc6, 0xb6, 0xc3, 0xbf, - 0xc8, 0xa4, 0xc6, 0xab, 0x5e, 0xc3, 0x9a, 0xc5, 0xa0, 0xe2, 0xb1, 0xb0, 0xc3, 0x90, - 0xc8, 0x99, 0xc5, 0xac, 0xe1, 0x9a, 0xa6, 0xc3, 0x95, 0xcd, 0xb4, 0xcd, 0xb3, 0xc6, - 0x9f, 0xc7, 0x80, 0xc8, 0xaa, 0xe1, 0x9a, 0xb7, 0xc7, 0xa6, 0x2c, 0xc5, 0x83, 0xc8, - 0xbe, 0xc8, 0x93, 0xc4, 0xa0, 0xc3, 0x9f, 0xc5, 0xa1, 0xc5, 0x8d, 0xe1, 0x9b, 0xa5, - 0xc6, 0x99, 0x4b, 0xc4, 0xbe, 0xc8, 0x87, 0xc8, 0x92, 0xe1, 0x9b, 0xae, 0xc8, 0x86, - 0xc7, 0x90, 0xc5, 0x85, 0xc2, 0xb6, 0xc5, 0x84, 0xe1, 0x9a, 0xa5, 0xc8, 0x86, 0xc4, - 0x8d, 0xc6, 0xb9, 0xc8, 0x90, 0x25, 0xc4, 0xa9, 0xc4, 0xbb, 0xc5, 0xb0, 0xe2, 0xb1, - 0xa9, 0xe1, 0x9b, 0xad, 0x60, 0xc4, 0x9b, 0xc8, 0xab, 0xc3, 0xad, 0xc6, 0xa2, 0xc7, - 0x9d, 0xe1, 0x9b, 0x89, 0xe1, 0x9b, 0x97, 0xe1, 0x9b, 0xa6, 0x32, 0xc6, 0x99, 0xc3, - 0x9e, 0xc9, 0x8e, 0x4c, 0xe1, 0x9b, 0x93, 0xc6, 0xa4, 0xc6, 0x8c, 0xe2, 0xb1, 0xb8, - 0xc7, 0x9f, 0x2a, 0xc5, 0xa1, 0xe1, 0x9b, 0x9f, 0xcd, 0xbb, 0xc3, 0x8f, 0xc7, 0x89, - 0xc7, 0xaf, 0x5e, 0xc7, 0x8b, 0xc8, 0xa4, 0xc6, 0x81, 0xcd, 0xb3, 0xc2, 0xa9, 0xc7, - 0xae, 0xc8, 0x83, 0xc6, 0xae, 0xe1, 0x9b, 0xa3, 0xc6, 0xa3, 0xc6, 0xa0, 0xe1, 0x9a, - 0xab, 0xc8, 0xab, 0xc5, 0x9c, 0xc8, 0x94, 0xc5, 0xa8, 0xe2, 0xb1, 0xb6, 0xc4, 0xae, - 0xc6, 0xbe, 0xc7, 0xa4, 0xc7, 0x87, 0xc8, 0x9d, 0xe1, 0x9a, 0xbf, 0xc4, 0xa7, 0xc3, - 0xb3, 0xc5, 0x9e, 0xe1, 0x9a, 0xa2, 0xe1, 0x9b, 0x9e, 0xc7, 0xad, 0x7e, 0xe1, 0x9b, - 0x93, 0xc6, 0xa8, 0xc4, 0xb5, 0xc6, 0xb1, 0xe1, 0x9b, 0xa0, 0xc4, 0xac, 0xc4, 0x91, - 0xc4, 0xbe, 0xc8, 0x89, 0x4f, 0xc6, 0xa2, 0xc5, 0xbb, 0xe1, 0x9b, 0x91, 0xc7, 0x9f, - 0xe1, 0x9b, 0xa4, 0xe1, 0x9b, 0x9a, 0xc7, 0xa9, 0xc4, 0xa6, 0xe1, 0x9b, 0x85, 0xc7, - 0xae, 0xc5, 0x91, 0xc6, 0x94, 0x42, 0x6c, 0xc4, 0xab, 0xc5, 0xa0, 0xc8, 0x89, 0xe1, - 0x9a, 0xb9, 0xc6, 0xac, 0xc6, 0xae, 0xc5, 0xa1, 0xcd, 0xb1, 0xc8, 0xb0, 0xc3, 0x84, - 0xc8, 0xbc, 0xc3, 0x9b, 0xc7, 0xbc, 0xc6, 0xbc, 0xc8, 0x82, 0xc4, 0xbf, 0xe1, 0x9b, - 0xaa, 0xc8, 0xae, 0xce, 0x85, 0xc6, 0x86, 0x4c, + 0xe1, 0x9b, 0xa9, 0xc4, 0x80, 0xc4, 0xbb, 0xe1, 0x9b, 0xa4, 0xc5, 0xa5, 0xc4, 0xac, + 0xc5, 0xbb, 0x7e, 0xc6, 0x82, 0xc5, 0xaf, 0xc3, 0xbd, 0x3b, 0xc8, 0x88, 0xc3, 0xaa, + 0xc6, 0x97, 0x2c, 0xc6, 0x82, 0x76, 0xe2, 0xb1, 0xa1, 0xc3, 0x9f, 0xc3, 0xa1, 0xc4, + 0xa2, 0xc4, 0x92, 0xc6, 0xa8, 0xc4, 0x8e, 0xe1, 0x9a, 0xbb, 0xc5, 0xac, 0xcd, 0xb5, + 0xc6, 0x80, 0xc6, 0xa5, 0xc8, 0xba, 0xc2, 0xa2, 0xc8, 0xb3, 0xc8, 0xa3, 0xc4, 0x98, + 0x65, 0xe1, 0x9b, 0xb0, 0xe1, 0x9b, 0xa1, 0xc3, 0x96, 0x5c, 0xc8, 0x81, 0xc5, 0x93, + 0xc6, 0xac, 0xc7, 0x9e, 0xc3, 0x8c, 0x53, 0xc7, 0xb9, 0xc4, 0x88, 0xc3, 0xa4, 0xc7, + 0x96, 0x64, 0xc5, 0xa7, 0xc4, 0x81, 0x2b, 0x7a, 0xe2, 0xb1, 0xa1, 0xc6, 0xa9, 0xe1, + 0x9b, 0xa1, 0xc8, 0xa4, 0xe1, 0x9b, 0x9b, 0xc5, 0xbf, 0xc6, 0x8d, 0xe2, 0xb1, 0xa9, + 0xc8, 0xab, 0xe2, 0xb1, 0xb1, 0xc3, 0x99, 0xc5, 0x96, 0xc7, 0x95, 0xc6, 0xad, 0xc3, + 0xac, 0xc3, 0x8a, 0x25, 0xc7, 0xa5, 0xc6, 0xa8, 0xe1, 0x9b, 0x80, 0xe2, 0xb1, 0xaf, + 0xe1, 0x9b, 0xa1, 0xc7, 0xa0, 0xe1, 0x9b, 0xa6, 0xc5, 0xa0, 0xc7, 0x85, 0xc4, 0x99, + 0xc6, 0xb7, 0x6e, 0xe1, 0x9b, 0xb0, 0x3b, 0x54, 0xc8, 0xa4, 0xc3, 0x89, 0xe1, 0x9a, + 0xa4, 0x75, 0xc3, 0x9f, 0xc6, 0x98, 0xc4, 0xb8, 0xe1, 0x9b, 0xa7, 0xe2, 0xb1, 0xbb, + 0xc5, 0xad, 0xc7, 0xbc, 0xc3, 0xaf, 0xc4, 0x9e, 0x61, 0xc8, 0x87, 0x61, 0xc5, 0x82, + 0xe1, 0x9b, 0x8d, 0xc5, 0xbe, 0xc5, 0xa3, 0xc2, 0xb3, 0xc7, 0xb3, 0xc6, 0x90, 0xc8, + 0x8b, 0xc6, 0x81, 0xe2, 0xb1, 0xb1, 0xc3, 0xa3, 0xc2, 0xa6, 0xe1, 0x9a, 0xb4, 0xe1, + 0x9b, 0x81, 0xc6, 0x94, 0xc2, 0xb1, 0xc5, 0xaf, 0xc7, 0x84, 0xc3, 0xa6, 0xc5, 0xb6, + 0xc3, 0x8a, 0x5c, 0xe1, 0x9b, 0x85, 0xe1, 0x9b, 0xa2, 0xc4, 0x99, 0xc2, 0xb4, 0xc5, + 0x9b, 0xc8, 0xa8, 0xc8, 0x8f, 0x42, 0xc4, 0x8e, 0xe1, 0x9b, 0xaa, 0xc8, 0x89, 0x6e, + 0xc5, 0xaf, 0xe1, 0x9a, 0xa8, 0xe1, 0x9a, 0xac, 0xc4, 0xa5, 0xc4, 0x8b, 0xc7, 0x8a, + 0xe1, 0x9b, 0x93, 0xc8, 0xba, 0xc4, 0xbc, 0xc2, 0xa3, 0xc8, 0x9e, 0xc6, 0xbf, 0xc6, + 0xb2, 0xcd, 0xbc, 0xc3, 0x9d, 0xc4, 0x98, 0xe1, 0x9b, 0x8f, 0xc4, 0x96, 0xc5, 0xa8, + 0x77, 0xc4, 0x8d, 0xc5, 0x88, 0xc7, 0xa2, 0xc3, 0x81, 0xc7, 0x89, 0xc4, 0x92, 0xe1, + 0x9a, 0xb7, 0xc7, 0xb1, 0xe2, 0xb1, 0xab, 0xc4, 0x9b, 0x5e, 0xc4, 0x8b, 0xe2, 0xb1, + 0xa0, 0xe1, 0x9b, 0xad, 0xcd, 0xbd, 0xe1, 0x9a, 0xa1, 0xe2, 0xb1, 0xbb, 0xe1, 0x9a, + 0xbf, 0xc8, 0xb1, 0xc6, 0x97, 0xcd, 0xbc, 0xc7, 0xb4, 0xc3, 0xb1, 0xe1, 0x9b, 0x86, + 0x5d, 0xcd, 0xb2, 0xc6, 0xb0, 0xc9, 0x81, 0xc5, 0xac, 0x4a, 0xc8, 0x97, 0xc7, 0x94, + 0x64, 0xc3, 0x80, 0xc5, 0xb8, 0xe1, 0x9b, 0x9b, 0xc4, 0x9a, 0xc8, 0xb5, 0xe1, 0x9b, + 0x88, 0xe1, 0x9a, 0xa2, 0xc7, 0xae, 0xc4, 0xb5, 0x33, 0xc2, 0xb4, 0xc3, 0xb3, 0xc7, + 0xb0, 0xc5, 0xbb, 0xe1, 0x9b, 0x98, 0xc8, 0xb6, 0x3d, 0x30, 0xe1, 0x9a, 0xa3, 0xc6, + 0x95, 0xc3, 0x90, 0xc8, 0xae, 0xc9, 0x8f, 0xc3, 0x9b, 0xc6, 0xa7, 0x6a, 0xe1, 0x9a, + 0xb8, 0xe1, 0x9a, 0xb5, 0xc4, 0xb0, 0xe1, 0x9b, 0x9d, 0x7b, 0xc6, 0xb1, 0xc6, 0xae, + 0x30, 0xc8, 0xa0, 0xc5, 0x98, 0xc5, 0x91, 0xe1, 0x9a, 0xa3, 0xe2, 0xb1, 0xaf, 0xe2, + 0xb1, 0xa9, 0xe1, 0x9b, 0x90, 0xc8, 0x89, 0xcd, 0xb4, 0xe1, 0x9b, 0x83, 0xc5, 0x88, + 0xc2, 0xa5, 0xc6, 0x9c, 0xc2, 0xac, 0xc2, 0xb2, 0xe1, 0x9b, 0x88, 0x6f, 0xe1, 0x9b, + 0x9b, 0xc5, 0xb4, 0xc6, 0xa7, 0xc8, 0x8f, 0x5a, ], asset_base: [ - 0x86, 0x78, 0x20, 0x68, 0xdc, 0x11, 0xd4, 0x4f, 0xff, 0xfe, 0x8d, 0x44, 0x03, 0xad, - 0x6e, 0xf1, 0xe7, 0x7d, 0x20, 0xd2, 0x10, 0x83, 0x2e, 0x2d, 0xbf, 0x84, 0xdf, 0x09, - 0x7c, 0x87, 0x21, 0x24, + 0x26, 0x21, 0x1d, 0x3b, 0x0b, 0xcf, 0x80, 0x41, 0x43, 0x30, 0x1b, 0x45, 0x23, 0x7b, + 0x96, 0xc4, 0x97, 0xe6, 0xe3, 0x87, 0x2c, 0x75, 0xb8, 0xc1, 0x58, 0x67, 0x31, 0xda, + 0x39, 0x1e, 0xc4, 0x25, ], }, ] diff --git a/src/test_vectors/issuance_auth_sig.rs b/src/test_vectors/issuance_auth_sig.rs index fe71486e2..648a5cfb4 100644 --- a/src/test_vectors/issuance_auth_sig.rs +++ b/src/test_vectors/issuance_auth_sig.rs @@ -1,4 +1,4 @@ -// From https://github.com/zcash-hackworks/zcash-test-vectors/ (issuance_auth_sig) +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_zsa_issuance_auth_sig) pub(crate) struct TestVector { pub(crate) isk: [u8; 32], diff --git a/src/test_vectors/keys.rs b/src/test_vectors/keys.rs index 4417713eb..56d189478 100644 --- a/src/test_vectors/keys.rs +++ b/src/test_vectors/keys.rs @@ -1,4 +1,4 @@ -// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_key_components) +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_zsa_key_components) pub(crate) struct TestVector { pub(crate) sk: [u8; 32], diff --git a/src/test_vectors/merkle_path.rs b/src/test_vectors/merkle_path.rs index ff0270d2d..930d7721d 100644 --- a/src/test_vectors/merkle_path.rs +++ b/src/test_vectors/merkle_path.rs @@ -1,3 +1,5 @@ +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_merkle_tree) + pub(crate) struct TestVector { pub(crate) leaves: [[u8; 32]; 16], pub(crate) paths: [[[u8; 32]; 4]; 16], @@ -5,7 +7,6 @@ pub(crate) struct TestVector { } pub(crate) fn test_vectors() -> Vec { - // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_merkle_tree.py vec![ TestVector { leaves: [ diff --git a/src/test_vectors/note_encryption.rs b/src/test_vectors/note_encryption_vanilla.rs similarity index 99% rename from src/test_vectors/note_encryption.rs rename to src/test_vectors/note_encryption_vanilla.rs index 7a5a73a56..b8abe6744 100644 --- a/src/test_vectors/note_encryption.rs +++ b/src/test_vectors/note_encryption_vanilla.rs @@ -1,5 +1,6 @@ +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_note_encryption) + pub(crate) struct TestVector { - // make all fields public so we can use them in the test pub(crate) incoming_viewing_key: [u8; 64], pub(crate) ovk: [u8; 32], pub(crate) default_d: [u8; 11], @@ -21,7 +22,6 @@ pub(crate) struct TestVector { pub(crate) c_out: [u8; 80], } -// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_note_encryption.py pub(crate) fn test_vectors() -> Vec { vec![ TestVector { diff --git a/src/test_vectors/note_encryption_v3.rs b/src/test_vectors/note_encryption_zsa.rs similarity index 99% rename from src/test_vectors/note_encryption_v3.rs rename to src/test_vectors/note_encryption_zsa.rs index 79b514869..cc7d3d4b6 100644 --- a/src/test_vectors/note_encryption_v3.rs +++ b/src/test_vectors/note_encryption_zsa.rs @@ -1,4 +1,4 @@ -// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_note_encryption) +// From https://github.com/zcash-hackworks/zcash-test-vectors/ (orchard_zsa_note_encryption) pub(crate) struct TestVector { pub(crate) incoming_viewing_key: [u8; 64], diff --git a/src/value.rs b/src/value.rs index ae3862726..b94844fd6 100644 --- a/src/value.rs +++ b/src/value.rs @@ -222,14 +222,16 @@ impl Neg for ValueSum { } impl<'a> Sum<&'a ValueSum> for Result { - fn sum>(iter: I) -> Self { - iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + *v).ok_or(OverflowError)) + fn sum>(mut iter: I) -> Self { + iter.try_fold(ValueSum(0), |acc, v| acc + *v) + .ok_or(OverflowError) } } impl Sum for Result { - fn sum>(iter: I) -> Self { - iter.fold(Ok(ValueSum(0)), |acc, v| (acc? + v).ok_or(OverflowError)) + fn sum>(mut iter: I) -> Self { + iter.try_fold(ValueSum(0), |acc, v| acc + v) + .ok_or(OverflowError) } } diff --git a/tests/builder.rs b/tests/builder.rs index 903ab3eca..b1b6aa946 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -6,7 +6,8 @@ use orchard::{ circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, note::{AssetBase, ExtractedNoteCommitment}, - note_encryption_v3::OrchardDomainV3, + note_encryption::OrchardDomainBase, + orchard_flavors::OrchardZSA, tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, Anchor, Bundle, Note, @@ -14,7 +15,13 @@ use orchard::{ use rand::rngs::OsRng; use zcash_note_encryption_zsa::try_note_decryption; -pub fn verify_bundle(bundle: &Bundle, vk: &VerifyingKey, verify_proof: bool) { +type OrchardDomainZSA = OrchardDomainBase; + +pub fn verify_bundle( + bundle: &Bundle, + vk: &VerifyingKey, + verify_proof: bool, +) { if verify_proof { assert!(matches!(bundle.verify_proof(vk), Ok(()))); } @@ -50,15 +57,16 @@ pub fn build_merkle_path(note: &Note) -> (MerklePath, Anchor) { #[test] fn bundle_chain() { let mut rng = OsRng; - let pk = ProvingKey::build(); - let vk = VerifyingKey::build(); + // FIXME: consider adding test for OrchardVanilla as well + let pk = ProvingKey::build::(); + let vk = VerifyingKey::build::(); let sk = SpendingKey::from_bytes([0; 32]).unwrap(); let fvk = FullViewingKey::from(&sk); let recipient = fvk.address_at(0u32, Scope::External); // Create a shielding bundle. - let shielding_bundle: Bundle<_, i64> = { + let shielding_bundle: Bundle<_, i64, OrchardZSA> = { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); @@ -83,13 +91,13 @@ fn bundle_chain() { verify_bundle(&shielding_bundle, &vk, true); // Create a shielded bundle spending the previous output. - let shielded_bundle: Bundle<_, i64> = { + let shielded_bundle: Bundle<_, i64, OrchardZSA> = { let ivk = PreparedIncomingViewingKey::new(&fvk.to_ivk(Scope::External)); let (note, _, _) = shielding_bundle .actions() .iter() .find_map(|action| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainZSA::for_action(action); try_note_decryption(&domain, &ivk, action) }) .unwrap(); diff --git a/tests/zsa.rs b/tests/zsa.rs index 40b75f80c..8278f5c2f 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -7,13 +7,15 @@ use orchard::bundle::Authorized; use orchard::issuance::{verify_issue_bundle, IssueBundle, IssueInfo, Signed, Unauthorized}; use orchard::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; use orchard::note::{AssetBase, ExtractedNoteCommitment}; -use orchard::note_encryption_v3::OrchardDomainV3; + use orchard::tree::{MerkleHashOrchard, MerklePath}; use orchard::{ builder::Builder, bundle::Flags, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, + note_encryption::OrchardDomainBase, + orchard_flavors::OrchardZSA, value::NoteValue, Address, Anchor, Bundle, Note, }; @@ -21,6 +23,8 @@ use rand::rngs::OsRng; use std::collections::HashSet; use zcash_note_encryption_zsa::try_note_decryption; +type OrchardDomainZSA = OrchardDomainBase; + #[derive(Debug)] struct Keychain { pk: ProvingKey, @@ -51,8 +55,9 @@ impl Keychain { } fn prepare_keys() -> Keychain { - let pk = ProvingKey::build(); - let vk = VerifyingKey::build(); + // FIXME: consider adding test for OrchardDomainVanilla as well + let pk = ProvingKey::build::(); + let vk = VerifyingKey::build::(); let sk = SpendingKey::from_bytes([0; 32]).unwrap(); let fvk = FullViewingKey::from(&sk); @@ -85,7 +90,7 @@ fn build_and_sign_bundle( mut rng: OsRng, pk: &ProvingKey, sk: &SpendingKey, -) -> Bundle { +) -> Bundle { let unauthorized = builder.build(&mut rng).unwrap(); let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(pk, &mut rng).unwrap(); @@ -180,7 +185,7 @@ fn issue_zsa_notes(asset_descr: &str, keys: &Keychain) -> (Note, Note) { fn create_native_note(keys: &Keychain) -> Note { let mut rng = OsRng; - let shielding_bundle: Bundle<_, i64> = { + let shielding_bundle: Bundle<_, i64, OrchardZSA> = { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); @@ -205,7 +210,7 @@ fn create_native_note(keys: &Keychain) -> Note { .actions() .iter() .find_map(|action| { - let domain = OrchardDomainV3::for_action(action); + let domain = OrchardDomainZSA::for_action(action); try_note_decryption(&domain, &PreparedIncomingViewingKey::new(&ivk), action) }) .unwrap(); @@ -238,7 +243,7 @@ fn build_and_verify_bundle( keys: &Keychain, ) -> Result<(), String> { let rng = OsRng; - let shielded_bundle: Bundle<_, i64> = { + let shielded_bundle: Bundle<_, i64, OrchardZSA> = { let mut builder = Builder::new(Flags::from_parts(true, true, true), anchor); spends @@ -266,7 +271,7 @@ fn build_and_verify_bundle( Ok(()) } -fn verify_unique_spent_nullifiers(bundle: &Bundle) -> bool { +fn verify_unique_spent_nullifiers(bundle: &Bundle) -> bool { let mut unique_nulifiers = Vec::new(); let spent_nullifiers = bundle .actions()