├── .gitignore ├── rust-toolchain ├── src ├── host │ ├── mod.rs │ └── rmd160.rs ├── lib.rs ├── utils │ └── mod.rs └── circuits │ ├── range.rs │ ├── mod.rs │ ├── rmd160.rs │ └── modexp.rs ├── Cargo.toml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2023-12-03 2 | -------------------------------------------------------------------------------- /src/host/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod rmd160; 2 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![feature(array_zip)] 2 | #![feature(slice_flatten)] 3 | pub mod circuits; 4 | pub mod host; 5 | pub mod utils; 6 | pub use halo2_proofs::halo2curves::bn256::Fr; 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "misc-precompiled-circuit" 3 | authors = ["xgao@zoyoe.com"] 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | name = "mylib" 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | strum = "0.25" 15 | strum_macros = "0.25" 16 | halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2022_09_10" } 17 | # FIXME 18 | halo2-gate-generator = { git = "https://github.com/scroll-tech/halo2gategen", branch = "scroll" } 19 | num-bigint = { version = "0.4", features = ["rand"] } 20 | rand = "0.8" 21 | serde = { version = "1.0", features = ["serde_derive"] } 22 | serde_json = "1.0" 23 | subtle = "2.4" 24 | 25 | [patch."https://github.com/privacy-scaling-explorations/halo2.git"] 26 | halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Define misc precompiled circuits including rmd160 and modexp. Try to unify the range check components so that all circuits only depend on one column for range check. 4 | 5 | Notice: the modexp circuit only supports U256 instead of arbitrary length bigint. This is a difference from EVM L1 behavior by design. 6 | 7 | # Generic $(a^b)_p$ over $\mathbb{F}_r$ in Halo2 8 | 9 | ## I. Representation of $u256$ values in $F_r$ 10 | Suppose that for each $x$, $x \in [0, 2^{256}-1]$. We represent $a$ as a vector of three $\mathbb{F}_r$ limbs $[x_0, x_1, x_2]$ such that $x_0 \le 2^{108}$, $x_1 \le 2^{108}$ and $x_2 \le 2^{108}$ 11 | 12 | $\langle$ x $\rangle$ = 13 | $\underbrace{b_{0} ,b_{1} , \cdots, b_{107}}$ $\bigl( x_0 \bigr)$ 14 | $\underbrace{b_{108},b_{109}, \cdots, b_{215}}$ $\bigl( x_1 \bigr)$ 15 | $\underbrace{b_{216},b_{217}, \cdots, b_{255}}$ $\bigl( x_2 \bigr)$ 16 | 17 | In addition we use $x_3$ to represent $x$ % $r$ and put them all together we represent $x = [x_0, x_1, x_2, x_3]$. 18 | 19 | 20 | ## II. Constraints of $xy = kp + d$ where $x, y, k, p \in [0, 2^{256}-1]$. 21 | 22 | ### 1. Picking coprime numbers $d_0, d_1, r$ such that $d_0 d_1 r > 2^{512}$. 23 | Picking $d_0 = 2^{108}-1$, $d_1 = 2^{216}$, and $r$. We notice that these three numbers are co-prime. 24 | 25 | **Proof:** 26 | $r$ is prime, $2^{216}$ has only one factor which is $2$ and $2^{108} - 1$ is an odd number. 27 | **Qed.** 28 | 29 | ### 2. By CRT the following constraints decides a unique $d$ for $x = kp + d$ 30 | 1. $\langle xy \rangle_{d_0} = \langle kp \rangle_{d_0} + \langle d \rangle_{d_0}$ 31 | 1. $\langle xy \rangle_{d_1} = \langle kp \rangle_{d_1} + \langle d \rangle_{d_1}$ 32 | 2. $\langle xy \rangle_{r} = \langle kp \rangle_{r} + \langle d \rangle_{r}$ 33 | 3. $d < p$. 34 | 35 | ### 3. Reminders of $\langle x \rangle$ under $d_0, d_1$ and $r$. 36 | 1. $\langle x \rangle_{d_0} = \langle x_2 + x_1 + x_0 \rangle_{d_0}$ 37 | 2. $\langle x \rangle_{d_1} = \langle [x_0, x_1, 0, 0]\rangle_{d_1}$ 38 | 3. $\langle x \rangle_{r} = x_3$ 39 | 40 | ### 4. Put it all together we get the following constraints. 41 | * $(x_0 + x_1 + x_2)(y_0 + y_1 + y_2) \\ = (k_0 + k_1 + k_2) (p_0 + p_1 + p2) + d_0 + d_1 + d_2$ (modular $2^{108} - 1$) 42 | * $(x_0y_0 + 2^{108} (x_1y_0 + x_0y_1)) = (k_0p_0 + d_0 + 2^{108} (p_1k_0 + p_0k_1) + d1)$ (modular $2^{216}$) 43 | * $x_3y_3 = k_3p_3 + d_3$ (modular $r$) 44 | * $d < p$. 45 | 46 | ## III. Finalize $modexp(a,b)$ over $p$ 47 | ### Pseudo code with simple double and add algorithm 48 | ``` 49 | let sum = 1; 50 | let b = [bi; 256]; 51 | for (i=0;i<256;i++) { 52 | sum = (sum * sum * a^bi) mod p 53 | } 54 | ``` 55 | ### TODO: Change to wnaf with window = 2 56 | 57 | ## IV. Gate Definition 58 | ### Use standard halo2 gate generator as following: 59 | ``` 60 | customized_circuits!(ModExpConfig, 2, 5, 9, 1, 61 | | l0 | l1 | l2 | l3 | d | c0 | c1 | c2 | c3 | cd | cdn | c | c03 | c12 | sel 62 | | nil | nil | nil | nil | d_n | nil | nil | nil | nil | nil | nil | nil | nil | nil | nil 63 | ); 64 | ``` 65 | where l0, l1, l2, l3, d are witness cells and c0, c1, c2, c3, cd, cdn, c, c03, c12 are constant cells. 66 | 67 | ### Gate constraint for one line: 68 | ``` 69 | cs.create_gate("one line constraint", |meta| { 70 | let l0 = config.get_expr(meta, ModExpConfig::l0()); 71 | let l1 = config.get_expr(meta, ModExpConfig::l1()); 72 | let l2 = config.get_expr(meta, ModExpConfig::l2()); 73 | let l3 = config.get_expr(meta, ModExpConfig::l3()); 74 | let d = config.get_expr(meta, ModExpConfig::d()); 75 | let dnext = config.get_expr(meta, ModExpConfig::d_n()); 76 | let c0 = config.get_expr(meta, ModExpConfig::c0()); 77 | let c1 = config.get_expr(meta, ModExpConfig::c1()); 78 | let c2 = config.get_expr(meta, ModExpConfig::c2()); 79 | let c3 = config.get_expr(meta, ModExpConfig::c3()); 80 | let c = config.get_expr(meta, ModExpConfig::c()); 81 | let cd = config.get_expr(meta, ModExpConfig::cd()); 82 | let cdn = config.get_expr(meta, ModExpConfig::cdn()); 83 | let c03 = config.get_expr(meta, ModExpConfig::c03()); 84 | let c12 = config.get_expr(meta, ModExpConfig::c12()); 85 | let sel = config.get_expr(meta, ModExpConfig::sel()); 86 | 87 | vec![ 88 | sel * ( 89 | l0.clone() * c0 90 | + l1.clone() * c1 91 | + l2.clone() * c2 92 | + l3.clone() * c3 93 | + d * cd 94 | + dnext * cdn 95 | + l0 * l3 * c03 96 | + l1 * l2 * c12 97 | + c) 98 | ] 99 | }); 100 | ``` 101 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use halo2_proofs::{ 2 | circuit::AssignedCell, 3 | halo2curves::ff::{FromUniformBytes, PrimeField}, 4 | }; 5 | use num_bigint::BigUint; 6 | 7 | pub fn field_to_bn(f: &F) -> BigUint { 8 | let bytes = f.to_repr(); 9 | // to_repr is little-endian as per the test below. 10 | BigUint::from_bytes_le(bytes.as_ref()) 11 | } 12 | 13 | pub fn bn_to_field>(bn: &BigUint) -> F { 14 | let mut bytes = bn.to_bytes_le(); 15 | bytes.resize(64, 0); 16 | F::from_uniform_bytes(&bytes.try_into().unwrap()) 17 | } 18 | 19 | pub fn field_to_u32(f: &F) -> u32 { 20 | // hmmm any better ways to handle this? 21 | let tmp: [u8; 4] = f 22 | .to_repr() 23 | .as_ref() 24 | .iter() 25 | .take(4) 26 | .copied() 27 | .collect::>() 28 | .try_into() 29 | .unwrap(); 30 | u32::from_le_bytes(tmp) 31 | } 32 | 33 | pub fn field_to_u64(f: &F) -> u64 { 34 | // hmmm any better ways to handle this? 35 | let tmp: [u8; 8] = f 36 | .to_repr() 37 | .as_ref() 38 | .iter() 39 | .take(8) 40 | .copied() 41 | .collect::>() 42 | .try_into() 43 | .unwrap(); 44 | u64::from_le_bytes(tmp) 45 | } 46 | 47 | pub fn u32_to_limbs(v: u32) -> [F; 4] { 48 | let mut rem = v; 49 | let mut r = vec![]; 50 | for _ in 0..4 { 51 | r.append(&mut vec![F::from((rem % 256) as u64)]); 52 | rem /= 256; 53 | } 54 | r.try_into().unwrap() 55 | } 56 | 57 | /* FIXME should not get value based on cell in new halo2 */ 58 | pub fn cell_to_value(cell: &AssignedCell) -> F { 59 | //cell.value().map_or(0, |x| field_to_u32(x)) 60 | let mut r = F::ZERO; 61 | cell.value().map(|x| r = *x); 62 | r 63 | } 64 | 65 | /* FIXME should not get value based on cell in new halo2 */ 66 | pub fn cell_to_u32(cell: &AssignedCell) -> u32 { 67 | //cell.value().map_or(0, |x| field_to_u32(x)) 68 | let mut r = 0; 69 | cell.value().map(|x| r = field_to_u32(x)); 70 | r 71 | } 72 | 73 | pub fn cell_to_limbs(cell: &AssignedCell) -> [F; 4] { 74 | let a = cell_to_u32(cell); 75 | u32_to_limbs(a) 76 | } 77 | 78 | #[macro_export] 79 | macro_rules! curr { 80 | ($meta: expr, $x: expr) => { 81 | $meta.query_advice($x, halo2_proofs::poly::Rotation::cur()) 82 | }; 83 | } 84 | 85 | #[macro_export] 86 | macro_rules! prev { 87 | ($meta: expr, $x: expr) => { 88 | $meta.query_advice($x, halo2_proofs::poly::Rotation::prev()) 89 | }; 90 | } 91 | 92 | #[macro_export] 93 | macro_rules! next { 94 | ($meta: expr, $x: expr) => { 95 | $meta.query_advice($x, halo2_proofs::poly::Rotation::next()) 96 | }; 97 | } 98 | 99 | #[macro_export] 100 | macro_rules! nextn { 101 | ($meta: expr, $x: expr, $n:expr) => { 102 | $meta.query_advice($x, halo2_proofs::poly::Rotation($n)) 103 | }; 104 | } 105 | 106 | #[macro_export] 107 | macro_rules! fixed_curr { 108 | ($meta: expr, $x: expr) => { 109 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::cur()) 110 | }; 111 | } 112 | 113 | #[macro_export] 114 | macro_rules! instance_curr { 115 | ($meta: expr, $x: expr) => { 116 | $meta.query_instance($x, halo2_proofs::poly::Rotation::cur()) 117 | }; 118 | } 119 | 120 | #[macro_export] 121 | macro_rules! fixed_prev { 122 | ($meta: expr, $x: expr) => { 123 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::prev()) 124 | }; 125 | } 126 | 127 | #[macro_export] 128 | macro_rules! fixed_next { 129 | ($meta: expr, $x: expr) => { 130 | $meta.query_fixed($x, halo2_proofs::poly::Rotation::next()) 131 | }; 132 | } 133 | 134 | #[macro_export] 135 | macro_rules! constant_from { 136 | ($x: expr) => { 137 | halo2_proofs::plonk::Expression::Constant(F::from($x as u64)) 138 | }; 139 | } 140 | 141 | #[macro_export] 142 | macro_rules! constant_from_bn { 143 | ($x: expr) => { 144 | halo2_proofs::plonk::Expression::Constant(bn_to_field($x)) 145 | }; 146 | } 147 | 148 | #[macro_export] 149 | macro_rules! constant { 150 | ($x: expr) => { 151 | halo2_proofs::plonk::Expression::Constant($x) 152 | }; 153 | } 154 | 155 | #[macro_export] 156 | macro_rules! value_for_assign { 157 | ($x: expr) => { 158 | halo2_proofs::circuit::Value::known($x) 159 | }; 160 | } 161 | 162 | #[cfg(test)] 163 | mod tests { 164 | use super::{bn_to_field, field_to_bn}; 165 | use halo2_proofs::halo2curves::{bn256::Fr, group::ff::PrimeField}; 166 | 167 | #[test] 168 | fn test_bn_field_roundtrip() { 169 | let repr = Fr::one().to_repr(); 170 | assert_eq!( 171 | repr, 172 | [ 173 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174 | 0, 0, 0, 0 175 | ], 176 | "F::to_repr() must be little-endian" 177 | ); 178 | 179 | let a = -Fr::one(); 180 | let b = bn_to_field(&field_to_bn(&a)); 181 | assert_eq!(a, b); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/circuits/range.rs: -------------------------------------------------------------------------------- 1 | use crate::circuits::{LookupAssistChip, LookupAssistConfig}; 2 | use crate::utils::{bn_to_field, field_to_bn}; 3 | use halo2_gate_generator::{ 4 | constant_from, customized_circuits, customized_circuits_expand, item_count, table_item, 5 | value_for_assign, GateCell, Limb, 6 | }; 7 | use halo2_proofs::halo2curves::ff::FromUniformBytes; 8 | use halo2_proofs::{ 9 | circuit::Region, 10 | halo2curves::ff::PrimeField, 11 | plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, VirtualCells}, 12 | poly::Rotation, 13 | }; 14 | use num_bigint::BigUint; 15 | use std::marker::PhantomData; 16 | use std::ops::Div; 17 | 18 | /* 19 | * Customized gates range_check(target) with each limb less than 2^12 20 | * acc will be the sum of the target limbs and rem is the remaining limbs 21 | * of the target value. 22 | */ 23 | customized_circuits!(RangeCheckConfig, 2, 3, 2, 0, |limb| acc 24 | | rem 25 | | table 26 | | sel 27 | | nil 28 | | acc_n 29 | | rem_n 30 | | nil 31 | | sel_n); 32 | 33 | impl LookupAssistConfig for RangeCheckConfig { 34 | /// register a column (col) to be range checked by limb size (sz) 35 | fn register( 36 | &self, 37 | cs: &mut ConstraintSystem, 38 | col: impl FnOnce(&mut VirtualCells) -> Expression, 39 | sz: impl FnOnce(&mut VirtualCells) -> Expression, 40 | ) { 41 | cs.lookup_any("check ranges", |meta| { 42 | let acc = self.get_expr(meta, RangeCheckConfig::acc()); 43 | let rem = self.get_expr(meta, RangeCheckConfig::rem()); 44 | vec![(col(meta), acc), (sz(meta), rem)] 45 | }); 46 | } 47 | } 48 | 49 | pub struct RangeCheckChip { 50 | config: RangeCheckConfig, 51 | offset: usize, 52 | _marker: PhantomData, 53 | } 54 | 55 | impl> LookupAssistChip for RangeCheckChip { 56 | fn provide_lookup_evidence( 57 | &mut self, 58 | region: &mut Region, 59 | value: F, 60 | sz: u64, 61 | ) -> Result<(), Error> { 62 | self.assign_value_with_range(region, value, sz) 63 | } 64 | } 65 | 66 | impl> RangeCheckChip { 67 | pub fn new(config: RangeCheckConfig) -> Self { 68 | RangeCheckChip { 69 | config, 70 | offset: 0, 71 | _marker: PhantomData, 72 | } 73 | } 74 | 75 | pub fn configure(cs: &mut ConstraintSystem) -> RangeCheckConfig { 76 | let witness = [0; 3].map(|_| cs.advice_column()); 77 | witness.map(|x| cs.enable_equality(x)); 78 | let fixed = [0; 2].map(|_| cs.fixed_column()); 79 | let selector = []; 80 | 81 | let config = RangeCheckConfig { 82 | fixed, 83 | selector, 84 | witness, 85 | }; 86 | 87 | // Range Check of all limbs 88 | // 89 | cs.lookup_any("within ranges", |meta| { 90 | let limb = config.get_expr(meta, RangeCheckConfig::limb()); 91 | let table = config.get_expr(meta, RangeCheckConfig::table()); 92 | vec![(limb, table)] 93 | }); 94 | 95 | // First we require the rem is continus if it is not zero 96 | cs.create_gate("range check constraint", |meta| { 97 | let rem = config.get_expr(meta, RangeCheckConfig::rem()); 98 | let rem_n = config.get_expr(meta, RangeCheckConfig::rem_n()); 99 | let sel = config.get_expr(meta, RangeCheckConfig::sel()); 100 | 101 | vec![sel * rem.clone() * (rem - rem_n - constant_from!(1))] 102 | }); 103 | 104 | // Second we make sure if the rem is not zero then 105 | // carry = carry_n * 2^12 + limb 106 | cs.create_gate("limb acc constraint", |meta| { 107 | let limb = config.get_expr(meta, RangeCheckConfig::limb()); 108 | let acc = config.get_expr(meta, RangeCheckConfig::acc()); 109 | let acc_n = config.get_expr(meta, RangeCheckConfig::acc_n()); 110 | let sel = config.get_expr(meta, RangeCheckConfig::sel()); 111 | let sel_n = config.get_expr(meta, RangeCheckConfig::sel_n()); 112 | 113 | vec![ 114 | sel.clone() * (acc.clone() - limb - acc_n * constant_from!(1u64 << 12) * sel_n), 115 | sel.clone() * (constant_from!(1) - sel.clone()), 116 | //(constant_from!(1) - sel) * acc, // if sel is 0 then acc must equal to 0 117 | ] 118 | }); 119 | cs.create_gate("end with zero", |meta| { 120 | let sel = config.get_expr(meta, RangeCheckConfig::sel()); 121 | let acc_n = config.get_expr(meta, RangeCheckConfig::acc_n()); 122 | let sel_n = config.get_expr(meta, RangeCheckConfig::sel_n()); 123 | vec![ 124 | sel * acc_n * (constant_from!(1) - sel_n), // if sel is 0 then acc must equal to 0 125 | ] 126 | }); 127 | 128 | config 129 | } 130 | 131 | /// Make sure the (value, sz) pair is lookupable in the range_chip 132 | pub fn assign_value_with_range( 133 | &mut self, 134 | region: &mut Region, 135 | value: F, 136 | sz: u64, 137 | ) -> Result<(), Error> { 138 | let mut limbs = vec![]; 139 | let mut bn = field_to_bn(&value); 140 | let mut cs = vec![]; 141 | for _ in 0..sz { 142 | cs.push(bn_to_field(&bn)); 143 | let limb = bn.modpow(&BigUint::from(1u128), &BigUint::from(1u128 << 12)); 144 | bn = (bn - limb.clone()).div(BigUint::from(1u128 << 12)); 145 | limbs.push(bn_to_field(&limb)); 146 | } 147 | cs.reverse(); 148 | limbs.reverse(); 149 | for i in 0..sz { 150 | let limb = limbs.pop().unwrap(); 151 | let acc = cs.pop().unwrap(); 152 | self.config 153 | .assign_cell(region, self.offset, &RangeCheckConfig::limb(), limb)?; 154 | self.config 155 | .assign_cell(region, self.offset, &RangeCheckConfig::acc(), acc)?; 156 | self.config.assign_cell( 157 | region, 158 | self.offset, 159 | &RangeCheckConfig::rem(), 160 | F::from_u128((sz - i) as u128), 161 | )?; 162 | self.config 163 | .assign_cell(region, self.offset, &RangeCheckConfig::sel(), F::ONE)?; 164 | self.offset += 1; 165 | } 166 | self.config 167 | .assign_cell(region, self.offset, &RangeCheckConfig::limb(), F::ZERO)?; 168 | self.config 169 | .assign_cell(region, self.offset, &RangeCheckConfig::acc(), F::ZERO)?; 170 | self.config 171 | .assign_cell(region, self.offset, &RangeCheckConfig::rem(), F::ZERO)?; 172 | self.config 173 | .assign_cell(region, self.offset, &RangeCheckConfig::sel(), F::ZERO)?; 174 | self.offset += 1; 175 | Ok(()) 176 | } 177 | 178 | /// initialize the table column from 1 to 2^12 179 | /// initialize needs to be called before using the range_chip 180 | pub fn initialize(&mut self, region: &mut Region) -> Result<(), Error> { 181 | for i in 0..4096 { 182 | self.config.assign_cell( 183 | region, 184 | i, 185 | &RangeCheckConfig::table(), 186 | F::from_u128(i as u128), 187 | )?; 188 | } 189 | self.offset = 0; 190 | self.assign_value_with_range(region, F::ZERO, 25)?; 191 | Ok(()) 192 | } 193 | } 194 | 195 | #[cfg(test)] 196 | mod tests { 197 | use halo2_proofs::dev::MockProver; 198 | use halo2_proofs::halo2curves::bn256::Fr; 199 | 200 | use halo2_proofs::{ 201 | circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner}, 202 | plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, VirtualCells}, 203 | poly::Rotation, 204 | }; 205 | 206 | use super::{RangeCheckChip, RangeCheckConfig}; 207 | use crate::circuits::LookupAssistConfig; 208 | use crate::value_for_assign; 209 | 210 | #[derive(Clone, Debug)] 211 | pub struct HelperChipConfig { 212 | limb: Column, 213 | } 214 | 215 | impl HelperChipConfig { 216 | pub fn range_check_column(&self, cs: &mut VirtualCells) -> Expression { 217 | cs.query_advice(self.limb, Rotation::cur()) 218 | } 219 | } 220 | 221 | #[derive(Clone, Debug)] 222 | pub struct HelperChip { 223 | config: HelperChipConfig, 224 | } 225 | 226 | impl Chip for HelperChip { 227 | type Config = HelperChipConfig; 228 | type Loaded = (); 229 | 230 | fn config(&self) -> &Self::Config { 231 | &self.config 232 | } 233 | 234 | fn loaded(&self) -> &Self::Loaded { 235 | &() 236 | } 237 | } 238 | 239 | impl HelperChip { 240 | fn new(config: HelperChipConfig) -> Self { 241 | HelperChip { config } 242 | } 243 | 244 | fn configure(cs: &mut ConstraintSystem) -> HelperChipConfig { 245 | let limb = cs.advice_column(); 246 | cs.enable_equality(limb); 247 | HelperChipConfig { limb } 248 | } 249 | 250 | fn assign_value( 251 | &self, 252 | region: &mut Region, 253 | offset: &mut usize, 254 | value: Fr, 255 | ) -> Result, Error> { 256 | let c = region.assign_advice( 257 | || format!("assign input"), 258 | self.config.limb, 259 | *offset, 260 | || value_for_assign!(value), 261 | )?; 262 | *offset = *offset + 1; 263 | Ok(c) 264 | } 265 | } 266 | 267 | #[derive(Clone, Debug, Default)] 268 | struct TestCircuit {} 269 | 270 | #[derive(Clone, Debug)] 271 | struct TestConfig { 272 | rangecheckconfig: RangeCheckConfig, 273 | helperconfig: HelperChipConfig, 274 | } 275 | 276 | impl Circuit for TestCircuit { 277 | type Config = TestConfig; 278 | type FloorPlanner = SimpleFloorPlanner; 279 | 280 | fn without_witnesses(&self) -> Self { 281 | Self::default() 282 | } 283 | 284 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 285 | let rangecheckconfig = RangeCheckChip::::configure(meta); 286 | let helperconfig = HelperChip::configure(meta); 287 | 288 | rangecheckconfig.register( 289 | meta, 290 | |c| helperconfig.range_check_column(c), 291 | |_| Expression::Constant(Fr::from(4 as u64)), 292 | ); 293 | 294 | Self::Config { 295 | rangecheckconfig, 296 | helperconfig, 297 | } 298 | } 299 | 300 | fn synthesize( 301 | &self, 302 | config: Self::Config, 303 | mut layouter: impl Layouter, 304 | ) -> Result<(), Error> { 305 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 306 | let helper_chip = HelperChip::new(config.clone().helperconfig); 307 | layouter.assign_region( 308 | || "range check test", 309 | |mut region| { 310 | let v = Fr::from(1u64 << 24 + 1); 311 | range_chip.initialize(&mut region)?; 312 | range_chip.assign_value_with_range(&mut region, v, 4)?; 313 | 314 | // assign helper 315 | let mut offset = 0; 316 | helper_chip.assign_value(&mut region, &mut offset, v)?; 317 | Ok(()) 318 | }, 319 | )?; 320 | Ok(()) 321 | } 322 | } 323 | 324 | #[test] 325 | fn test_range_circuit() { 326 | let test_circuit = TestCircuit {}; 327 | let prover = MockProver::run(18, &test_circuit, vec![]).unwrap(); 328 | assert_eq!(prover.verify(), Ok(())); 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ark-std" 7 | version = "0.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" 10 | dependencies = [ 11 | "num-traits", 12 | "rand", 13 | ] 14 | 15 | [[package]] 16 | name = "arrayref" 17 | version = "0.3.7" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" 20 | 21 | [[package]] 22 | name = "arrayvec" 23 | version = "0.7.4" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 26 | 27 | [[package]] 28 | name = "autocfg" 29 | version = "1.1.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 32 | 33 | [[package]] 34 | name = "bitvec" 35 | version = "1.0.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 38 | dependencies = [ 39 | "funty", 40 | "radium", 41 | "tap", 42 | "wyz", 43 | ] 44 | 45 | [[package]] 46 | name = "blake2b_simd" 47 | version = "1.0.2" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" 50 | dependencies = [ 51 | "arrayref", 52 | "arrayvec", 53 | "constant_time_eq", 54 | ] 55 | 56 | [[package]] 57 | name = "block-buffer" 58 | version = "0.9.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 61 | dependencies = [ 62 | "block-padding", 63 | "generic-array", 64 | ] 65 | 66 | [[package]] 67 | name = "block-padding" 68 | version = "0.2.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 71 | 72 | [[package]] 73 | name = "cfg-if" 74 | version = "0.1.10" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 77 | 78 | [[package]] 79 | name = "cfg-if" 80 | version = "1.0.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 83 | 84 | [[package]] 85 | name = "constant_time_eq" 86 | version = "0.3.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" 89 | 90 | [[package]] 91 | name = "cpufeatures" 92 | version = "0.2.11" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" 95 | dependencies = [ 96 | "libc", 97 | ] 98 | 99 | [[package]] 100 | name = "crossbeam" 101 | version = "0.8.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" 104 | dependencies = [ 105 | "cfg-if 1.0.0", 106 | "crossbeam-channel", 107 | "crossbeam-deque", 108 | "crossbeam-epoch", 109 | "crossbeam-queue", 110 | "crossbeam-utils", 111 | ] 112 | 113 | [[package]] 114 | name = "crossbeam-channel" 115 | version = "0.5.8" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 118 | dependencies = [ 119 | "cfg-if 1.0.0", 120 | "crossbeam-utils", 121 | ] 122 | 123 | [[package]] 124 | name = "crossbeam-deque" 125 | version = "0.8.3" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 128 | dependencies = [ 129 | "cfg-if 1.0.0", 130 | "crossbeam-epoch", 131 | "crossbeam-utils", 132 | ] 133 | 134 | [[package]] 135 | name = "crossbeam-epoch" 136 | version = "0.9.15" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 139 | dependencies = [ 140 | "autocfg", 141 | "cfg-if 1.0.0", 142 | "crossbeam-utils", 143 | "memoffset", 144 | "scopeguard", 145 | ] 146 | 147 | [[package]] 148 | name = "crossbeam-queue" 149 | version = "0.3.8" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" 152 | dependencies = [ 153 | "cfg-if 1.0.0", 154 | "crossbeam-utils", 155 | ] 156 | 157 | [[package]] 158 | name = "crossbeam-utils" 159 | version = "0.8.16" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 162 | dependencies = [ 163 | "cfg-if 1.0.0", 164 | ] 165 | 166 | [[package]] 167 | name = "digest" 168 | version = "0.9.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 171 | dependencies = [ 172 | "generic-array", 173 | ] 174 | 175 | [[package]] 176 | name = "either" 177 | version = "1.9.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 180 | 181 | [[package]] 182 | name = "ff" 183 | version = "0.13.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" 186 | dependencies = [ 187 | "bitvec", 188 | "rand_core", 189 | "subtle", 190 | ] 191 | 192 | [[package]] 193 | name = "funty" 194 | version = "2.0.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 197 | 198 | [[package]] 199 | name = "generic-array" 200 | version = "0.14.7" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 203 | dependencies = [ 204 | "typenum", 205 | "version_check", 206 | ] 207 | 208 | [[package]] 209 | name = "getrandom" 210 | version = "0.2.10" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 213 | dependencies = [ 214 | "cfg-if 1.0.0", 215 | "libc", 216 | "wasi", 217 | ] 218 | 219 | [[package]] 220 | name = "group" 221 | version = "0.13.0" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 224 | dependencies = [ 225 | "ff", 226 | "rand_core", 227 | "subtle", 228 | ] 229 | 230 | [[package]] 231 | name = "halo2-gate-generator" 232 | version = "0.1.0" 233 | source = "git+https://github.com/scroll-tech/halo2gategen#8ccf462e1eff4ed0e602d7ba19771b2c53dee0e3" 234 | dependencies = [ 235 | "halo2_proofs", 236 | "lazy_static", 237 | "num-bigint", 238 | "rand", 239 | "serde", 240 | "serde_json", 241 | "strum 0.24.1", 242 | "strum_macros 0.24.3", 243 | "subtle", 244 | ] 245 | 246 | [[package]] 247 | name = "halo2_proofs" 248 | version = "0.2.0" 249 | source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.0#44dbed4515984bbc14cb8283e273794b03c28afa" 250 | dependencies = [ 251 | "ark-std", 252 | "blake2b_simd", 253 | "cfg-if 0.1.10", 254 | "crossbeam", 255 | "ff", 256 | "group", 257 | "halo2curves", 258 | "log", 259 | "num-bigint", 260 | "num-integer", 261 | "poseidon", 262 | "rand_core", 263 | "rayon", 264 | "sha3", 265 | "subtle", 266 | "tracing", 267 | ] 268 | 269 | [[package]] 270 | name = "halo2curves" 271 | version = "0.1.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab" 274 | dependencies = [ 275 | "blake2b_simd", 276 | "ff", 277 | "group", 278 | "lazy_static", 279 | "num-bigint", 280 | "num-traits", 281 | "pasta_curves", 282 | "paste", 283 | "rand", 284 | "rand_core", 285 | "serde", 286 | "serde_arrays", 287 | "static_assertions", 288 | "subtle", 289 | ] 290 | 291 | [[package]] 292 | name = "heck" 293 | version = "0.4.1" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 296 | 297 | [[package]] 298 | name = "itoa" 299 | version = "1.0.9" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 302 | 303 | [[package]] 304 | name = "keccak" 305 | version = "0.1.4" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" 308 | dependencies = [ 309 | "cpufeatures", 310 | ] 311 | 312 | [[package]] 313 | name = "lazy_static" 314 | version = "1.4.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 317 | dependencies = [ 318 | "spin", 319 | ] 320 | 321 | [[package]] 322 | name = "libc" 323 | version = "0.2.150" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 326 | 327 | [[package]] 328 | name = "log" 329 | version = "0.4.20" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 332 | 333 | [[package]] 334 | name = "memoffset" 335 | version = "0.9.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 338 | dependencies = [ 339 | "autocfg", 340 | ] 341 | 342 | [[package]] 343 | name = "misc-precompiled-circuit" 344 | version = "0.1.0" 345 | dependencies = [ 346 | "halo2-gate-generator", 347 | "halo2_proofs", 348 | "num-bigint", 349 | "rand", 350 | "serde", 351 | "serde_json", 352 | "strum 0.25.0", 353 | "strum_macros 0.25.3", 354 | "subtle", 355 | ] 356 | 357 | [[package]] 358 | name = "num-bigint" 359 | version = "0.4.4" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" 362 | dependencies = [ 363 | "autocfg", 364 | "num-integer", 365 | "num-traits", 366 | "rand", 367 | ] 368 | 369 | [[package]] 370 | name = "num-integer" 371 | version = "0.1.45" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 374 | dependencies = [ 375 | "autocfg", 376 | "num-traits", 377 | ] 378 | 379 | [[package]] 380 | name = "num-traits" 381 | version = "0.2.17" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 384 | dependencies = [ 385 | "autocfg", 386 | ] 387 | 388 | [[package]] 389 | name = "once_cell" 390 | version = "1.18.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 393 | 394 | [[package]] 395 | name = "opaque-debug" 396 | version = "0.3.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 399 | 400 | [[package]] 401 | name = "pasta_curves" 402 | version = "0.5.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" 405 | dependencies = [ 406 | "blake2b_simd", 407 | "ff", 408 | "group", 409 | "lazy_static", 410 | "rand", 411 | "static_assertions", 412 | "subtle", 413 | ] 414 | 415 | [[package]] 416 | name = "paste" 417 | version = "1.0.14" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 420 | 421 | [[package]] 422 | name = "pin-project-lite" 423 | version = "0.2.13" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 426 | 427 | [[package]] 428 | name = "poseidon" 429 | version = "0.2.0" 430 | source = "git+https://github.com/scroll-tech/poseidon.git?branch=main#5787dd3d2ce7a9e9601a035c396ac0c03449b54d" 431 | dependencies = [ 432 | "halo2curves", 433 | "subtle", 434 | ] 435 | 436 | [[package]] 437 | name = "ppv-lite86" 438 | version = "0.2.17" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 441 | 442 | [[package]] 443 | name = "proc-macro2" 444 | version = "1.0.70" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" 447 | dependencies = [ 448 | "unicode-ident", 449 | ] 450 | 451 | [[package]] 452 | name = "quote" 453 | version = "1.0.33" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 456 | dependencies = [ 457 | "proc-macro2", 458 | ] 459 | 460 | [[package]] 461 | name = "radium" 462 | version = "0.7.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 465 | 466 | [[package]] 467 | name = "rand" 468 | version = "0.8.5" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 471 | dependencies = [ 472 | "libc", 473 | "rand_chacha", 474 | "rand_core", 475 | ] 476 | 477 | [[package]] 478 | name = "rand_chacha" 479 | version = "0.3.1" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 482 | dependencies = [ 483 | "ppv-lite86", 484 | "rand_core", 485 | ] 486 | 487 | [[package]] 488 | name = "rand_core" 489 | version = "0.6.4" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 492 | dependencies = [ 493 | "getrandom", 494 | ] 495 | 496 | [[package]] 497 | name = "rayon" 498 | version = "1.8.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" 501 | dependencies = [ 502 | "either", 503 | "rayon-core", 504 | ] 505 | 506 | [[package]] 507 | name = "rayon-core" 508 | version = "1.12.0" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" 511 | dependencies = [ 512 | "crossbeam-deque", 513 | "crossbeam-utils", 514 | ] 515 | 516 | [[package]] 517 | name = "rustversion" 518 | version = "1.0.14" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 521 | 522 | [[package]] 523 | name = "ryu" 524 | version = "1.0.15" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 527 | 528 | [[package]] 529 | name = "scopeguard" 530 | version = "1.2.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 533 | 534 | [[package]] 535 | name = "serde" 536 | version = "1.0.193" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" 539 | dependencies = [ 540 | "serde_derive", 541 | ] 542 | 543 | [[package]] 544 | name = "serde_arrays" 545 | version = "0.1.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" 548 | dependencies = [ 549 | "serde", 550 | ] 551 | 552 | [[package]] 553 | name = "serde_derive" 554 | version = "1.0.193" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" 557 | dependencies = [ 558 | "proc-macro2", 559 | "quote", 560 | "syn 2.0.39", 561 | ] 562 | 563 | [[package]] 564 | name = "serde_json" 565 | version = "1.0.108" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 568 | dependencies = [ 569 | "itoa", 570 | "ryu", 571 | "serde", 572 | ] 573 | 574 | [[package]] 575 | name = "sha3" 576 | version = "0.9.1" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" 579 | dependencies = [ 580 | "block-buffer", 581 | "digest", 582 | "keccak", 583 | "opaque-debug", 584 | ] 585 | 586 | [[package]] 587 | name = "spin" 588 | version = "0.5.2" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 591 | 592 | [[package]] 593 | name = "static_assertions" 594 | version = "1.1.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 597 | 598 | [[package]] 599 | name = "strum" 600 | version = "0.24.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 603 | 604 | [[package]] 605 | name = "strum" 606 | version = "0.25.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" 609 | 610 | [[package]] 611 | name = "strum_macros" 612 | version = "0.24.3" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 615 | dependencies = [ 616 | "heck", 617 | "proc-macro2", 618 | "quote", 619 | "rustversion", 620 | "syn 1.0.109", 621 | ] 622 | 623 | [[package]] 624 | name = "strum_macros" 625 | version = "0.25.3" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" 628 | dependencies = [ 629 | "heck", 630 | "proc-macro2", 631 | "quote", 632 | "rustversion", 633 | "syn 2.0.39", 634 | ] 635 | 636 | [[package]] 637 | name = "subtle" 638 | version = "2.5.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 641 | 642 | [[package]] 643 | name = "syn" 644 | version = "1.0.109" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 647 | dependencies = [ 648 | "proc-macro2", 649 | "quote", 650 | "unicode-ident", 651 | ] 652 | 653 | [[package]] 654 | name = "syn" 655 | version = "2.0.39" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" 658 | dependencies = [ 659 | "proc-macro2", 660 | "quote", 661 | "unicode-ident", 662 | ] 663 | 664 | [[package]] 665 | name = "tap" 666 | version = "1.0.1" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 669 | 670 | [[package]] 671 | name = "tracing" 672 | version = "0.1.40" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 675 | dependencies = [ 676 | "pin-project-lite", 677 | "tracing-attributes", 678 | "tracing-core", 679 | ] 680 | 681 | [[package]] 682 | name = "tracing-attributes" 683 | version = "0.1.27" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 686 | dependencies = [ 687 | "proc-macro2", 688 | "quote", 689 | "syn 2.0.39", 690 | ] 691 | 692 | [[package]] 693 | name = "tracing-core" 694 | version = "0.1.32" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 697 | dependencies = [ 698 | "once_cell", 699 | ] 700 | 701 | [[package]] 702 | name = "typenum" 703 | version = "1.17.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 706 | 707 | [[package]] 708 | name = "unicode-ident" 709 | version = "1.0.12" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 712 | 713 | [[package]] 714 | name = "version_check" 715 | version = "0.9.4" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 718 | 719 | [[package]] 720 | name = "wasi" 721 | version = "0.11.0+wasi-snapshot-preview1" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 724 | 725 | [[package]] 726 | name = "wyz" 727 | version = "0.5.1" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 730 | dependencies = [ 731 | "tap", 732 | ] 733 | -------------------------------------------------------------------------------- /src/circuits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod modexp; 2 | pub mod range; 3 | pub mod rmd160; 4 | 5 | use crate::utils::field_to_bn; 6 | 7 | use halo2_gate_generator::{ 8 | customized_circuits, customized_circuits_expand, item_count, table_item, value_for_assign, 9 | GateCell, 10 | }; 11 | 12 | use halo2_proofs::{ 13 | circuit::Region, 14 | halo2curves::ff::PrimeField, 15 | plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, VirtualCells}, 16 | poly::Rotation, 17 | }; 18 | 19 | pub use halo2_gate_generator::Limb; 20 | 21 | /* 22 | * Customized gates for some of the common host circuits. 23 | * lookup_hint: lookup information that is usually combined with l0 24 | * lookup_ind: whether perform lookup at this line 25 | */ 26 | customized_circuits!(CommonGateConfig, 2, 5, 12, 0, |l0| l1 27 | | l2 28 | | l3 29 | | d 30 | | c0 31 | | c1 32 | | c2 33 | | c3 34 | | cd 35 | | cdn 36 | | c 37 | | c03 38 | | c12 39 | | lookup_hint 40 | | lookup_ind 41 | | sel 42 | | nil 43 | | nil 44 | | nil 45 | | nil 46 | | d_n 47 | | nil 48 | | nil 49 | | nil 50 | | nil 51 | | nil 52 | | nil 53 | | nil 54 | | nil 55 | | nil 56 | | nil 57 | | nil 58 | | nil); 59 | 60 | pub trait LookupAssistConfig { 61 | /// register a column (col) to be range checked by limb size (sz) 62 | fn register( 63 | &self, 64 | cs: &mut ConstraintSystem, 65 | col: impl FnOnce(&mut VirtualCells) -> Expression, 66 | sz: impl FnOnce(&mut VirtualCells) -> Expression, 67 | ); 68 | } 69 | 70 | pub trait LookupAssistChip { 71 | fn provide_lookup_evidence( 72 | &mut self, 73 | region: &mut Region, 74 | value: F, 75 | sz: u64, 76 | ) -> Result<(), Error>; 77 | } 78 | 79 | impl CommonGateConfig { 80 | pub fn configure( 81 | cs: &mut ConstraintSystem, 82 | lookup_assist_config: &LC, 83 | ) -> Self { 84 | let witness = [0; 5].map(|_| cs.advice_column()); 85 | witness.map(|x| cs.enable_equality(x)); 86 | let fixed = [0; 12].map(|_| cs.fixed_column()); 87 | let selector = []; 88 | 89 | let config = CommonGateConfig { 90 | fixed, 91 | selector, 92 | witness, 93 | }; 94 | 95 | lookup_assist_config.register( 96 | cs, 97 | |c| { 98 | config.get_expr(c, CommonGateConfig::l0()) 99 | * config.get_expr(c, CommonGateConfig::lookup_ind()) 100 | }, 101 | |c| config.get_expr(c, CommonGateConfig::lookup_hint()), 102 | ); 103 | 104 | cs.create_gate("one line constraint", |meta| { 105 | let l0 = config.get_expr(meta, CommonGateConfig::l0()); 106 | let l1 = config.get_expr(meta, CommonGateConfig::l1()); 107 | let l2 = config.get_expr(meta, CommonGateConfig::l2()); 108 | let l3 = config.get_expr(meta, CommonGateConfig::l3()); 109 | let d = config.get_expr(meta, CommonGateConfig::d()); 110 | let dnext = config.get_expr(meta, CommonGateConfig::d_n()); 111 | let c0 = config.get_expr(meta, CommonGateConfig::c0()); 112 | let c1 = config.get_expr(meta, CommonGateConfig::c1()); 113 | let c2 = config.get_expr(meta, CommonGateConfig::c2()); 114 | let c3 = config.get_expr(meta, CommonGateConfig::c3()); 115 | let c = config.get_expr(meta, CommonGateConfig::c()); 116 | let cd = config.get_expr(meta, CommonGateConfig::cd()); 117 | let cdn = config.get_expr(meta, CommonGateConfig::cdn()); 118 | let c03 = config.get_expr(meta, CommonGateConfig::c03()); 119 | let c12 = config.get_expr(meta, CommonGateConfig::c12()); 120 | let sel = config.get_expr(meta, CommonGateConfig::sel()); 121 | 122 | // if odd then carry is put at right else put at left 123 | vec![ 124 | sel * (l0.clone() * c0 125 | + l1.clone() * c1 126 | + l2.clone() * c2 127 | + l3.clone() * c3 128 | + d * cd 129 | + dnext * cdn 130 | + l0 * l3 * c03 131 | + l1 * l2 * c12 132 | + c), 133 | ] 134 | }); 135 | 136 | config 137 | } 138 | 139 | /// Select between f and t: if cond then t else f 140 | pub fn select>( 141 | &self, 142 | region: &mut Region, 143 | lookup_assist_chip: &mut LC, 144 | offset: &mut usize, 145 | cond: &Limb, 146 | f: &Limb, 147 | t: &Limb, 148 | hint: u64, 149 | ) -> Result, Error> { 150 | let result = if cond.value == F::ZERO { 151 | Limb::new(None, f.value) 152 | } else { 153 | Limb::new(None, t.value) 154 | }; 155 | let l = self.assign_line( 156 | region, 157 | lookup_assist_chip, 158 | offset, 159 | [ 160 | Some(t.clone()), 161 | Some(f.clone()), 162 | Some(cond.clone()), 163 | Some(cond.clone()), 164 | Some(result.clone()), 165 | None, 166 | ], 167 | [ 168 | None, 169 | Some(F::ONE), 170 | None, 171 | None, 172 | Some(-F::ONE), 173 | None, 174 | Some(F::ONE), 175 | Some(-F::ONE), 176 | None, 177 | ], 178 | hint, 179 | )?; 180 | Ok(l[4].clone()) 181 | } 182 | 183 | /// 184 | /// decompose a limb into binary cells, in big endian 185 | /// limbsize needs to be a multiple of 4 186 | pub fn decompose_limb>( 187 | &self, 188 | region: &mut Region, 189 | lookup_assist_chip: &mut LC, 190 | offset: &mut usize, 191 | limb: &Limb, 192 | limbs: &mut Vec>, 193 | limbsize: usize, 194 | ) -> Result<(), Error> { 195 | assert!(limbsize % 4 == 0); 196 | let mut bool_limbs = field_to_bn(&limb.value).to_radix_le(2); 197 | bool_limbs.truncate(limbsize); 198 | bool_limbs.resize_with(limbsize, || 0); 199 | bool_limbs.reverse(); 200 | let mut v = F::ZERO; 201 | for i in 0..(limbsize / 4) { 202 | let l0 = F::from_u128(bool_limbs[4 * i] as u128); 203 | let l1 = F::from_u128(bool_limbs[4 * i + 1] as u128); 204 | let l2 = F::from_u128(bool_limbs[4 * i + 2] as u128); 205 | let l3 = F::from_u128(bool_limbs[4 * i + 3] as u128); 206 | let v_next = v * F::from_u128(16u128) 207 | + l0 * F::from_u128(8u128) 208 | + l1 * F::from_u128(4u128) 209 | + l2 * F::from_u128(2u128) 210 | + l3 * F::from_u128(1u128); 211 | let l = self.assign_line( 212 | region, 213 | lookup_assist_chip, 214 | offset, 215 | [ 216 | Some(Limb::new(None, l0)), 217 | Some(Limb::new(None, l1)), 218 | Some(Limb::new(None, l2)), 219 | Some(Limb::new(None, l3)), 220 | Some(Limb::new(None, v)), 221 | Some(Limb::new(None, v_next)), 222 | ], 223 | [ 224 | Some(F::from_u128(8u128)), 225 | Some(F::from_u128(4u128)), 226 | Some(F::from_u128(2u128)), 227 | Some(F::from_u128(1u128)), 228 | Some(F::from_u128(16u128)), 229 | Some(-F::ONE), 230 | None, 231 | None, 232 | None, 233 | ], 234 | 0, 235 | )?; 236 | limbs.append(&mut l.to_vec()[0..4].to_vec()); 237 | v = v_next; 238 | } 239 | // constraint that limb.value is equal v_next so that the above limbs is 240 | // a real decompose of the limb.value 241 | self.assign_line( 242 | region, 243 | lookup_assist_chip, 244 | offset, 245 | [ 246 | Some(limb.clone()), 247 | None, 248 | None, 249 | None, 250 | Some(Limb::new(None, v)), 251 | None, 252 | ], 253 | [ 254 | Some(F::ONE), 255 | None, 256 | None, 257 | None, 258 | Some(-F::ONE), 259 | None, 260 | None, 261 | None, 262 | None, 263 | ], 264 | 0, 265 | )?; 266 | 267 | // the following constrains all the limbs to be either 1 or 0 (bool type) 268 | 269 | // apply eqn: (val * val) - val = 0, 270 | // by: (ws[1] * ws[2] * cs[7]) + (ws[0] * cs[0]) = 0, 271 | for lm in limbs.iter() { 272 | let _l = self.assign_line( 273 | region, 274 | lookup_assist_chip, 275 | offset, 276 | [ 277 | Some(lm.clone()), 278 | Some(lm.clone()), 279 | Some(lm.clone()), 280 | None, 281 | None, 282 | None, 283 | ], 284 | [ 285 | Some(-F::ONE), 286 | None, 287 | None, 288 | None, 289 | None, 290 | None, 291 | None, 292 | Some(F::ONE), 293 | None, 294 | ], 295 | 0, //what is limbbound 296 | )?; 297 | } 298 | 299 | Ok(()) 300 | } 301 | 302 | /// put pure witness advices with no constraints. 303 | pub fn assign_witness>( 304 | &self, 305 | region: &mut Region, 306 | _lookup_assist_chip: &mut LC, 307 | offset: &mut usize, 308 | value: [Option>; 5], 309 | hint: u64, // the boundary limit of the first cell 310 | ) -> Result>, Error> { 311 | let witnesses = [ 312 | CommonGateConfig::l0(), 313 | CommonGateConfig::l1(), 314 | CommonGateConfig::l2(), 315 | CommonGateConfig::l3(), 316 | CommonGateConfig::d(), 317 | ]; 318 | let mut limbs = vec![]; 319 | for i in 0..5 { 320 | let v = value[i].as_ref().map_or(F::ZERO, |x| x.value); 321 | let limb = self.assign_cell(region, *offset, &witnesses[i], v).unwrap(); 322 | if let Some(x) = value[i].clone() { 323 | limbs.push(limb.clone()); 324 | if let Some(c) = x.cell.as_ref() { 325 | region 326 | .constrain_equal(limb.get_the_cell().cell(), c.cell()) 327 | .unwrap(); 328 | } 329 | } 330 | } 331 | self.assign_cell( 332 | region, 333 | *offset, 334 | &CommonGateConfig::lookup_hint(), 335 | F::from(hint), 336 | )?; 337 | self.assign_cell( 338 | region, 339 | *offset, 340 | &CommonGateConfig::lookup_ind(), 341 | F::from(if hint == 0 { 0u64 } else { 1u64 }), 342 | )?; 343 | 344 | *offset += 1; 345 | Ok(limbs) 346 | } 347 | 348 | fn assign_line>( 349 | &self, 350 | region: &mut Region, 351 | lookup_assist_chip: &mut LC, 352 | offset: &mut usize, 353 | value: [Option>; 6], 354 | coeffs: [Option; 9], 355 | hint: u64, // the boundary limit of the first cell 356 | ) -> Result>, Error> { 357 | let ws = value 358 | .clone() 359 | .to_vec() 360 | .iter() 361 | .map(|x| x.clone().map_or(F::ZERO, |x| x.value)) 362 | .collect::>(); 363 | let cs = coeffs 364 | .clone() 365 | .to_vec() 366 | .iter() 367 | .map(|x| x.map_or(F::ZERO, |x| x)) 368 | .collect::>(); 369 | assert!( 370 | ws[0] * cs[0] 371 | + ws[1] * cs[1] 372 | + ws[2] * cs[2] 373 | + ws[3] * cs[3] 374 | + ws[4] * cs[4] 375 | + ws[5] * cs[5] 376 | + ws[0] * ws[3] * cs[6] 377 | + ws[1] * ws[2] * cs[7] 378 | + cs[8] 379 | == F::ZERO 380 | ); 381 | 382 | let witnesses = [ 383 | CommonGateConfig::l0(), 384 | CommonGateConfig::l1(), 385 | CommonGateConfig::l2(), 386 | CommonGateConfig::l3(), 387 | CommonGateConfig::d(), 388 | CommonGateConfig::d_n(), 389 | ]; 390 | let cs = [ 391 | CommonGateConfig::c0(), 392 | CommonGateConfig::c1(), 393 | CommonGateConfig::c2(), 394 | CommonGateConfig::c3(), 395 | CommonGateConfig::cd(), 396 | CommonGateConfig::cdn(), 397 | CommonGateConfig::c03(), 398 | CommonGateConfig::c12(), 399 | CommonGateConfig::c(), 400 | ]; 401 | 402 | let mut limbs = vec![]; 403 | for i in 0..6 { 404 | let v = value[i].as_ref().map_or(F::ZERO, |x| x.value); 405 | let limb = self.assign_cell(region, *offset, &witnesses[i], v).unwrap(); 406 | if let Some(x) = value[i].clone() { 407 | limbs.push(limb.clone()); 408 | if let Some(c) = x.cell { 409 | region 410 | .constrain_equal(limb.get_the_cell().cell(), c.cell()) 411 | .unwrap(); 412 | } 413 | } 414 | } 415 | for i in 0..9 { 416 | let v = coeffs[i].as_ref().map_or(F::ZERO, |x| *x); 417 | self.assign_cell(region, *offset, &cs[i], v).unwrap(); 418 | } 419 | self.assign_cell(region, *offset, &CommonGateConfig::sel(), F::ONE)?; 420 | self.assign_cell( 421 | region, 422 | *offset, 423 | &CommonGateConfig::lookup_hint(), 424 | F::from(hint), 425 | )?; 426 | self.assign_cell( 427 | region, 428 | *offset, 429 | &CommonGateConfig::lookup_ind(), 430 | F::from(if hint == 0 { 0u64 } else { 1u64 }), 431 | )?; 432 | 433 | if hint != 0 { 434 | lookup_assist_chip.provide_lookup_evidence( 435 | region, 436 | value[0].as_ref().unwrap().value, 437 | hint, 438 | )?; 439 | }; 440 | 441 | *offset += 1; 442 | Ok(limbs) 443 | } 444 | 445 | pub fn assign_constant>( 446 | &self, 447 | region: &mut Region, 448 | lookup_assist_chip: &mut LC, 449 | offset: &mut usize, 450 | value: &F, 451 | ) -> Result, Error> { 452 | let l = self.assign_line( 453 | region, 454 | lookup_assist_chip, 455 | offset, 456 | [Some(Limb::new(None, *value)), None, None, None, None, None], 457 | [ 458 | Some(F::ONE), 459 | None, 460 | None, 461 | None, 462 | None, 463 | None, 464 | None, 465 | None, 466 | Some(-*value), 467 | ], 468 | 0, 469 | )?; 470 | Ok(l[0].clone()) 471 | } 472 | 473 | /// check if limb is equal to constant F 474 | pub fn eq_constant>( 475 | &self, 476 | region: &mut Region, 477 | lookup_assist_chip: &mut LC, 478 | offset: &mut usize, 479 | limb: &Limb, 480 | constant: &F, 481 | ) -> Result, Error> { 482 | let delta = limb.value - constant; 483 | // ((limb.value - constant) * r) 484 | // ((inv * (limb.value - constant)) - (1-r)) 485 | let (inv, r) = if delta.is_zero_vartime() { 486 | (F::ONE, F::ONE) 487 | } else { 488 | (delta.invert().unwrap(), F::ZERO) 489 | }; 490 | let diff = self.sum_with_constant( 491 | region, 492 | lookup_assist_chip, 493 | offset, 494 | vec![(limb, F::ONE)], 495 | Some(-*constant), 496 | )?; 497 | let r = self.assign_line( 498 | region, 499 | lookup_assist_chip, 500 | offset, 501 | [ 502 | Some(diff.clone()), 503 | None, 504 | None, 505 | Some(Limb::new(None, r)), 506 | None, 507 | None, 508 | ], 509 | [None, None, None, None, None, None, Some(F::ONE), None, None], 510 | 0, 511 | )?; 512 | let r = r[1].clone(); 513 | let l = self.assign_line( 514 | region, 515 | lookup_assist_chip, 516 | offset, 517 | [ 518 | Some(Limb::new(None, inv)), 519 | Some(r), 520 | None, 521 | Some(diff), 522 | None, 523 | None, 524 | ], 525 | [ 526 | None, 527 | Some(F::ONE), 528 | None, 529 | None, 530 | None, 531 | None, 532 | Some(F::ONE), 533 | None, 534 | Some(-F::ONE), 535 | ], 536 | 0, 537 | )?; 538 | Ok(l[1].clone()) 539 | } 540 | 541 | pub fn sum_with_constant>( 542 | &self, 543 | region: &mut Region, 544 | lookup_assist_chip: &mut LC, 545 | offset: &mut usize, 546 | inputs: Vec<(&Limb, F)>, 547 | constant: Option, 548 | ) -> Result, Error> { 549 | let mut acc = F::ZERO; 550 | let mut firstline = true; 551 | let operands = inputs.clone(); 552 | let mut r = None; 553 | for chunk in operands.chunks(4) { 554 | let result = chunk.iter().fold(acc, |acc, &(l, v)| acc + l.value * v); 555 | if inputs.len() <= 3 { 556 | // solve it in oneline 557 | let result = result + constant.map_or(F::ZERO, |x| x); 558 | let mut limbs = chunk 559 | .iter() 560 | .map(|&(l, _v)| Some(l.clone())) 561 | .collect::>>>(); 562 | let mut coeffs = chunk 563 | .iter() 564 | .map(|&(_l, v)| Some(v)) 565 | .collect::>>(); 566 | limbs.resize_with(3, || None); 567 | coeffs.resize_with(3, || None); 568 | limbs.append(&mut vec![ 569 | Some(Limb::new(None, result)), 570 | Some(Limb::new(None, acc)), 571 | None, 572 | ]); 573 | coeffs.append(&mut vec![ 574 | Some(-F::ONE), 575 | if firstline { None } else { Some(F::ONE) }, 576 | None, 577 | None, 578 | None, 579 | constant, 580 | ]); 581 | let l = self.assign_line( 582 | region, 583 | lookup_assist_chip, 584 | offset, 585 | limbs.try_into().unwrap(), 586 | coeffs.try_into().unwrap(), 587 | 0, 588 | )?; 589 | r = Some(l.get(l.len() - 2).unwrap().clone()); 590 | } else { 591 | let mut limbs = chunk 592 | .iter() 593 | .map(|&(l, _v)| Some(l.clone())) 594 | .collect::>>>(); 595 | let mut coeffs = chunk 596 | .iter() 597 | .map(|&(_l, v)| Some(v)) 598 | .collect::>>(); 599 | limbs.resize_with(4, || None); 600 | coeffs.resize_with(4, || None); 601 | limbs.append(&mut vec![ 602 | Some(Limb::new(None, acc)), 603 | Some(Limb::new(None, result)), 604 | ]); 605 | coeffs.append(&mut vec![Some(F::ONE), Some(-F::ONE), None, None, None]); 606 | self.assign_line( 607 | region, 608 | lookup_assist_chip, 609 | offset, 610 | limbs.try_into().unwrap(), 611 | coeffs.try_into().unwrap(), 612 | 0, 613 | )?; 614 | } 615 | acc = result; 616 | firstline = false; 617 | } 618 | Ok(r.unwrap_or_else(|| { 619 | let result = acc + constant.map_or(F::ZERO, |x| x); 620 | // collect the last acc as result 621 | self.assign_line( 622 | region, 623 | lookup_assist_chip, 624 | offset, 625 | [ 626 | Some(Limb::new(None, result)), 627 | None, 628 | None, 629 | None, 630 | Some(Limb::new(None, acc)), 631 | None, 632 | ], 633 | [ 634 | Some(-F::ONE), 635 | None, 636 | None, 637 | None, 638 | Some(F::ONE), 639 | None, 640 | None, 641 | None, 642 | constant, 643 | ], 644 | 0, 645 | ) 646 | .unwrap() 647 | .into_iter() 648 | .next() 649 | .unwrap() 650 | })) 651 | } 652 | } 653 | -------------------------------------------------------------------------------- /src/host/rmd160.rs: -------------------------------------------------------------------------------- 1 | pub const DIGEST_BUF_LEN: usize = 5; 2 | pub const WORK_BUF_LEN: usize = 16; 3 | pub const H0: [u32; DIGEST_BUF_LEN] = [ 4 | 0x6745_2301, 5 | 0xefcd_ab89, 6 | 0x98ba_dcfe, 7 | 0x1032_5476, 8 | 0xc3d2_e1f0, 9 | ]; 10 | 11 | pub trait RMD160Atomic { 12 | fn f(x: Self, y: Self, z: Self) -> Self; 13 | fn g(x: Self, y: Self, z: Self) -> Self; 14 | fn h(x: Self, y: Self, z: Self) -> Self; 15 | fn i(x: Self, y: Self, z: Self) -> Self; 16 | fn j(x: Self, y: Self, z: Self) -> Self; 17 | fn atomic(round: usize, x: u32, y: u32, z: u32) -> u32; 18 | } 19 | 20 | impl RMD160Atomic for u32 { 21 | fn f(x: u32, y: u32, z: u32) -> u32 { 22 | x ^ y ^ z 23 | } 24 | fn g(x: u32, y: u32, z: u32) -> u32 { 25 | (x & y) | ((!x) & z) 26 | } 27 | fn h(x: u32, y: u32, z: u32) -> u32 { 28 | (x | (!y)) ^ z 29 | } 30 | fn i(x: u32, y: u32, z: u32) -> u32 { 31 | (x & z) | (y & (!z)) 32 | } 33 | 34 | fn j(x: u32, y: u32, z: u32) -> u32 { 35 | x ^ (y | (!z)) 36 | } 37 | 38 | fn atomic(round: usize, x: u32, y: u32, z: u32) -> u32 { 39 | match round { 40 | 0 => u32::f(x, y, z), 41 | 1 => u32::g(x, y, z), 42 | 2 => u32::h(x, y, z), 43 | 3 => u32::i(x, y, z), 44 | 4 => u32::j(x, y, z), 45 | _ => unreachable!(), 46 | } 47 | } 48 | } 49 | 50 | fn rol_modifier(round: usize, rol: &mut Vec, x: u32, offset: u32, shift: u32) { 51 | let v = match round { 52 | 0 => u32::f(rol[1], rol[2], rol[3]), 53 | 1 => u32::g(rol[1], rol[2], rol[3]), 54 | 2 => u32::h(rol[1], rol[2], rol[3]), 55 | 3 => u32::i(rol[1], rol[2], rol[3]), 56 | 4 => u32::j(rol[1], rol[2], rol[3]), 57 | _ => unreachable!(), 58 | }; 59 | rol[0] = rol[0].wrapping_add(v).wrapping_add(x).wrapping_add(offset); 60 | rol[0] = rol[0].rotate_left(shift).wrapping_add(rol[4]); 61 | rol[2] = rol[2].rotate_left(10); 62 | } 63 | 64 | pub(crate) const ROUNDS_OFFSET: [u32; DIGEST_BUF_LEN] = 65 | [0x0, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; 66 | 67 | pub(crate) const PROUNDS_OFFSET: [u32; DIGEST_BUF_LEN] = 68 | [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x0]; 69 | 70 | /*round1*/ 71 | pub(crate) const R: [[u32; 16]; DIGEST_BUF_LEN] = [ 72 | [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], 73 | [7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12], 74 | [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5], 75 | [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12], 76 | [9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6], 77 | ]; 78 | 79 | pub(crate) const O: [[usize; 16]; DIGEST_BUF_LEN] = [ 80 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 81 | [7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8], 82 | [3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12], 83 | [1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2], 84 | [4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13], 85 | ]; 86 | 87 | /*parallelround1*/ 88 | pub(crate) const PR: [[u32; 16]; DIGEST_BUF_LEN] = [ 89 | [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6], 90 | [9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11], 91 | [9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5], 92 | [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8], 93 | [8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11], 94 | ]; 95 | 96 | pub(crate) const PO: [[usize; 16]; DIGEST_BUF_LEN] = [ 97 | [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12], 98 | [6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2], 99 | [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13], 100 | [8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14], 101 | [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11], 102 | ]; 103 | 104 | pub fn compress(w: &[u32], values: Vec) -> Vec { 105 | let mut rol1 = w.to_vec(); 106 | let mut rol2 = w.to_vec(); 107 | let mut round = 0; 108 | for ((idxs, shift), offset) in O.iter().zip(R).zip(ROUNDS_OFFSET) { 109 | for limb_index in 0..16 { 110 | let idx = idxs[limb_index]; 111 | rol_modifier(round, &mut rol1, values[idx], offset, shift[limb_index]); 112 | rol1.rotate_right(1); 113 | } 114 | round += 1; 115 | } 116 | 117 | for ((idxs, shift), offset) in PO.iter().zip(PR).zip(PROUNDS_OFFSET) { 118 | for limb_index in 0..16 { 119 | let idx = idxs[limb_index]; 120 | rol_modifier(round - 1, &mut rol2, values[idx], offset, shift[limb_index]); 121 | rol2.rotate_right(1); 122 | } 123 | round -= 1; 124 | } 125 | 126 | println!("{:?}, {:?}", rol1, rol2); 127 | let mut r = vec![]; 128 | let len = w.len(); 129 | for i in 0..w.len() { 130 | r.push( 131 | w[i].wrapping_add(rol1[(i + 1) % len]) 132 | .wrapping_add(rol2[(i + 2) % len]), 133 | ); 134 | } 135 | r.rotate_left(1); 136 | println!("compressed {:?}", r); 137 | r 138 | } 139 | 140 | #[cfg(test)] 141 | mod tests { 142 | 143 | macro_rules! round( 144 | ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, 145 | $x:expr, $bits:expr, $add:expr, $round:expr) => ({ 146 | $a = $a.wrapping_add($round).wrapping_add($x).wrapping_add($add); 147 | $a = $a.rotate_left($bits).wrapping_add($e); 148 | $c = $c.rotate_left(10); 149 | }); 150 | ); 151 | 152 | macro_rules! process_block( 153 | ($h:ident, $data:expr, 154 | $( round1: h_ordering $f0:expr, $f1:expr, $f2:expr, $f3:expr, $f4:expr; 155 | data_index $data_index1:expr; roll_shift $bits1:expr )*; 156 | $( round2: h_ordering $g0:expr, $g1:expr, $g2:expr, $g3:expr, $g4:expr; 157 | data_index $data_index2:expr; roll_shift $bits2:expr )*; 158 | $( round3: h_ordering $h0:expr, $h1:expr, $h2:expr, $h3:expr, $h4:expr; 159 | data_index $data_index3:expr; roll_shift $bits3:expr )*; 160 | $( round4: h_ordering $i0:expr, $i1:expr, $i2:expr, $i3:expr, $i4:expr; 161 | data_index $data_index4:expr; roll_shift $bits4:expr )*; 162 | $( round5: h_ordering $j0:expr, $j1:expr, $j2:expr, $j3:expr, $j4:expr; 163 | data_index $data_index5:expr; roll_shift $bits5:expr )*; 164 | $( par_round1: h_ordering $pj0:expr, $pj1:expr, $pj2:expr, $pj3:expr, $pj4:expr; 165 | data_index $pdata_index1:expr; roll_shift $pbits1:expr )*; 166 | $( par_round2: h_ordering $pi0:expr, $pi1:expr, $pi2:expr, $pi3:expr, $pi4:expr; 167 | data_index $pdata_index2:expr; roll_shift $pbits2:expr )*; 168 | $( par_round3: h_ordering $ph0:expr, $ph1:expr, $ph2:expr, $ph3:expr, $ph4:expr; 169 | data_index $pdata_index3:expr; roll_shift $pbits3:expr )*; 170 | $( par_round4: h_ordering $pg0:expr, $pg1:expr, $pg2:expr, $pg3:expr, $pg4:expr; 171 | data_index $pdata_index4:expr; roll_shift $pbits4:expr )*; 172 | $( par_round5: h_ordering $pf0:expr, $pf1:expr, $pf2:expr, $pf3:expr, $pf4:expr; 173 | data_index $pdata_index5:expr; roll_shift $pbits5:expr )*; 174 | ) => ({ 175 | let mut bb = *$h; 176 | let mut bbb = *$h; 177 | 178 | // Round 1 179 | $( round!(bb[$f0], bb[$f1], bb[$f2], bb[$f3], bb[$f4], 180 | $data[$data_index1], $bits1, 0x0000_0000, 181 | bb[$f1] ^ bb[$f2] ^ bb[$f3]); )* 182 | 183 | // Round 2 184 | $( round!(bb[$g0], bb[$g1], bb[$g2], bb[$g3], bb[$g4], 185 | $data[$data_index2], $bits2, 0x5a82_7999, 186 | (bb[$g1] & bb[$g2]) | (!bb[$g1] & bb[$g3])); )* 187 | 188 | // Round 3 189 | $( round!(bb[$h0], bb[$h1], bb[$h2], bb[$h3], bb[$h4], 190 | $data[$data_index3], $bits3, 0x6ed9_eba1, 191 | (bb[$h1] | !bb[$h2]) ^ bb[$h3]); )* 192 | 193 | // Round 4 194 | $( round!(bb[$i0], bb[$i1], bb[$i2], bb[$i3], bb[$i4], 195 | $data[$data_index4], $bits4, 0x8f1b_bcdc, 196 | (bb[$i1] & bb[$i3]) | (bb[$i2] & !bb[$i3])); )* 197 | 198 | // Round 5 199 | $( round!(bb[$j0], bb[$j1], bb[$j2], bb[$j3], bb[$j4], 200 | $data[$data_index5], $bits5, 0xa953_fd4e, 201 | bb[$j1] ^ (bb[$j2] | !bb[$j3])); )* 202 | 203 | // Parallel rounds: these are the same as the previous five 204 | // rounds except that the constants have changed, we work 205 | // with the other buffer, and they are applied in reverse 206 | // order. 207 | 208 | // Parallel Round 1 209 | $( round!(bbb[$pj0], bbb[$pj1], bbb[$pj2], bbb[$pj3], bbb[$pj4], 210 | $data[$pdata_index1], $pbits1, 0x50a2_8be6, 211 | bbb[$pj1] ^ (bbb[$pj2] | !bbb[$pj3])); )* 212 | 213 | // Parallel Round 2 214 | $( round!(bbb[$pi0], bbb[$pi1], bbb[$pi2], bbb[$pi3], bbb[$pi4], 215 | $data[$pdata_index2], $pbits2, 0x5c4d_d124, 216 | (bbb[$pi1] & bbb[$pi3]) | (bbb[$pi2] & !bbb[$pi3])); )* 217 | 218 | // Parallel Round 3 219 | $( round!(bbb[$ph0], bbb[$ph1], bbb[$ph2], bbb[$ph3], bbb[$ph4], 220 | $data[$pdata_index3], $pbits3, 0x6d70_3ef3, 221 | (bbb[$ph1] | !bbb[$ph2]) ^ bbb[$ph3]); )* 222 | 223 | // Parallel Round 4 224 | $( round!(bbb[$pg0], bbb[$pg1], bbb[$pg2], bbb[$pg3], bbb[$pg4], 225 | $data[$pdata_index4], $pbits4, 0x7a6d_76e9, 226 | (bbb[$pg1] & bbb[$pg2]) | (!bbb[$pg1] & bbb[$pg3])); )* 227 | 228 | // Parallel Round 5 229 | $( round!(bbb[$pf0], bbb[$pf1], bbb[$pf2], bbb[$pf3], bbb[$pf4], 230 | $data[$pdata_index5], $pbits5, 0x0000_0000, 231 | bbb[$pf1] ^ bbb[$pf2] ^ bbb[$pf3]); )* 232 | 233 | // Combine results 234 | bbb[3] = bbb[3].wrapping_add($h[1]).wrapping_add(bb[2]); 235 | $h[1] = $h[2].wrapping_add(bb[3]).wrapping_add(bbb[4]); 236 | $h[2] = $h[3].wrapping_add(bb[4]).wrapping_add(bbb[0]); 237 | $h[3] = $h[4].wrapping_add(bb[0]).wrapping_add(bbb[1]); 238 | $h[4] = $h[0].wrapping_add(bb[1]).wrapping_add(bbb[2]); 239 | $h[0] = bbb[3]; 240 | }); 241 | ); 242 | 243 | pub fn compress(h: &mut [u32; super::DIGEST_BUF_LEN], data: &[u8; 64]) { 244 | let mut w = [0u32; super::WORK_BUF_LEN]; 245 | for (o, chunk) in w.iter_mut().zip(data.chunks_exact(4)) { 246 | *o = u32::from_le_bytes(chunk.try_into().unwrap()); 247 | } 248 | process_block!(h, w[..], 249 | // Round 1 250 | round1: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 11 251 | round1: h_ordering 4, 0, 1, 2, 3; data_index 1; roll_shift 14 252 | round1: h_ordering 3, 4, 0, 1, 2; data_index 2; roll_shift 15 253 | round1: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 12 254 | round1: h_ordering 1, 2, 3, 4, 0; data_index 4; roll_shift 5 255 | round1: h_ordering 0, 1, 2, 3, 4; data_index 5; roll_shift 8 256 | round1: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 7 257 | round1: h_ordering 3, 4, 0, 1, 2; data_index 7; roll_shift 9 258 | round1: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 11 259 | round1: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 13 260 | round1: h_ordering 0, 1, 2, 3, 4; data_index 10; roll_shift 14 261 | round1: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 15 262 | round1: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 6 263 | round1: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 7 264 | round1: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 9 265 | round1: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 8; 266 | 267 | // Round 2 268 | round2: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 7 269 | round2: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 6 270 | round2: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 8 271 | round2: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 13 272 | round2: h_ordering 0, 1, 2, 3, 4; data_index 10; roll_shift 11 273 | round2: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 9 274 | round2: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 7 275 | round2: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 15 276 | round2: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 7 277 | round2: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 12 278 | round2: h_ordering 4, 0, 1, 2, 3; data_index 9; roll_shift 15 279 | round2: h_ordering 3, 4, 0, 1, 2; data_index 5; roll_shift 9 280 | round2: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 11 281 | round2: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 7 282 | round2: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 13 283 | round2: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 12; 284 | 285 | // Round 3 286 | round3: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 11 287 | round3: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 13 288 | round3: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 6 289 | round3: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 7 290 | round3: h_ordering 4, 0, 1, 2, 3; data_index 9; roll_shift 14 291 | round3: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 9 292 | round3: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 13 293 | round3: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 15 294 | round3: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 14 295 | round3: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 8 296 | round3: h_ordering 3, 4, 0, 1, 2; data_index 0; roll_shift 13 297 | round3: h_ordering 2, 3, 4, 0, 1; data_index 6; roll_shift 6 298 | round3: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 5 299 | round3: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 12 300 | round3: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 7 301 | round3: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 5; 302 | 303 | // Round 4 304 | round4: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 11 305 | round4: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 12 306 | round4: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 14 307 | round4: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 15 308 | round4: h_ordering 3, 4, 0, 1, 2; data_index 0; roll_shift 14 309 | round4: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 15 310 | round4: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 9 311 | round4: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 8 312 | round4: h_ordering 4, 0, 1, 2, 3; data_index 13; roll_shift 9 313 | round4: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 14 314 | round4: h_ordering 2, 3, 4, 0, 1; data_index 7; roll_shift 5 315 | round4: h_ordering 1, 2, 3, 4, 0; data_index 15; roll_shift 6 316 | round4: h_ordering 0, 1, 2, 3, 4; data_index 14; roll_shift 8 317 | round4: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 6 318 | round4: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 5 319 | round4: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 12; 320 | 321 | // Round 5 322 | round5: h_ordering 1, 2, 3, 4, 0; data_index 4; roll_shift 9 323 | round5: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 15 324 | round5: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 5 325 | round5: h_ordering 3, 4, 0, 1, 2; data_index 9; roll_shift 11 326 | round5: h_ordering 2, 3, 4, 0, 1; data_index 7; roll_shift 6 327 | round5: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 8 328 | round5: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 13 329 | round5: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 12 330 | round5: h_ordering 3, 4, 0, 1, 2; data_index 14; roll_shift 5 331 | round5: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 12 332 | round5: h_ordering 1, 2, 3, 4, 0; data_index 3; roll_shift 13 333 | round5: h_ordering 0, 1, 2, 3, 4; data_index 8; roll_shift 14 334 | round5: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 11 335 | round5: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 8 336 | round5: h_ordering 2, 3, 4, 0, 1; data_index 15; roll_shift 5 337 | round5: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 6; 338 | 339 | // Parallel Round 1 340 | par_round1: h_ordering 0, 1, 2, 3, 4; data_index 5; roll_shift 8 341 | par_round1: h_ordering 4, 0, 1, 2, 3; data_index 14; roll_shift 9 342 | par_round1: h_ordering 3, 4, 0, 1, 2; data_index 7; roll_shift 9 343 | par_round1: h_ordering 2, 3, 4, 0, 1; data_index 0; roll_shift 11 344 | par_round1: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 13 345 | par_round1: h_ordering 0, 1, 2, 3, 4; data_index 2; roll_shift 15 346 | par_round1: h_ordering 4, 0, 1, 2, 3; data_index 11; roll_shift 15 347 | par_round1: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 5 348 | par_round1: h_ordering 2, 3, 4, 0, 1; data_index 13; roll_shift 7 349 | par_round1: h_ordering 1, 2, 3, 4, 0; data_index 6; roll_shift 7 350 | par_round1: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 8 351 | par_round1: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 11 352 | par_round1: h_ordering 3, 4, 0, 1, 2; data_index 1; roll_shift 14 353 | par_round1: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 14 354 | par_round1: h_ordering 1, 2, 3, 4, 0; data_index 3; roll_shift 12 355 | par_round1: h_ordering 0, 1, 2, 3, 4; data_index 12; roll_shift 6; 356 | 357 | // Parallel Round 2 358 | par_round2: h_ordering 4, 0, 1, 2, 3; data_index 6; roll_shift 9 359 | par_round2: h_ordering 3, 4, 0, 1, 2; data_index 11; roll_shift 13 360 | par_round2: h_ordering 2, 3, 4, 0, 1; data_index 3; roll_shift 15 361 | par_round2: h_ordering 1, 2, 3, 4, 0; data_index 7; roll_shift 7 362 | par_round2: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 12 363 | par_round2: h_ordering 4, 0, 1, 2, 3; data_index 13; roll_shift 8 364 | par_round2: h_ordering 3, 4, 0, 1, 2; data_index 5; roll_shift 9 365 | par_round2: h_ordering 2, 3, 4, 0, 1; data_index 10; roll_shift 11 366 | par_round2: h_ordering 1, 2, 3, 4, 0; data_index 14; roll_shift 7 367 | par_round2: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 7 368 | par_round2: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 12 369 | par_round2: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 7 370 | par_round2: h_ordering 2, 3, 4, 0, 1; data_index 4; roll_shift 6 371 | par_round2: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 15 372 | par_round2: h_ordering 0, 1, 2, 3, 4; data_index 1; roll_shift 13 373 | par_round2: h_ordering 4, 0, 1, 2, 3; data_index 2; roll_shift 11; 374 | 375 | // Parallel Round 3 376 | par_round3: h_ordering 3, 4, 0, 1, 2; data_index 15; roll_shift 9 377 | par_round3: h_ordering 2, 3, 4, 0, 1; data_index 5; roll_shift 7 378 | par_round3: h_ordering 1, 2, 3, 4, 0; data_index 1; roll_shift 15 379 | par_round3: h_ordering 0, 1, 2, 3, 4; data_index 3; roll_shift 11 380 | par_round3: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 8 381 | par_round3: h_ordering 3, 4, 0, 1, 2; data_index 14; roll_shift 6 382 | par_round3: h_ordering 2, 3, 4, 0, 1; data_index 6; roll_shift 6 383 | par_round3: h_ordering 1, 2, 3, 4, 0; data_index 9; roll_shift 14 384 | par_round3: h_ordering 0, 1, 2, 3, 4; data_index 11; roll_shift 12 385 | par_round3: h_ordering 4, 0, 1, 2, 3; data_index 8; roll_shift 13 386 | par_round3: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 5 387 | par_round3: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 14 388 | par_round3: h_ordering 1, 2, 3, 4, 0; data_index 10; roll_shift 13 389 | par_round3: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 13 390 | par_round3: h_ordering 4, 0, 1, 2, 3; data_index 4; roll_shift 7 391 | par_round3: h_ordering 3, 4, 0, 1, 2; data_index 13; roll_shift 5; 392 | 393 | // Parallel Round 4 394 | par_round4: h_ordering 2, 3, 4, 0, 1; data_index 8; roll_shift 15 395 | par_round4: h_ordering 1, 2, 3, 4, 0; data_index 6; roll_shift 5 396 | par_round4: h_ordering 0, 1, 2, 3, 4; data_index 4; roll_shift 8 397 | par_round4: h_ordering 4, 0, 1, 2, 3; data_index 1; roll_shift 11 398 | par_round4: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 14 399 | par_round4: h_ordering 2, 3, 4, 0, 1; data_index 11; roll_shift 14 400 | par_round4: h_ordering 1, 2, 3, 4, 0; data_index 15; roll_shift 6 401 | par_round4: h_ordering 0, 1, 2, 3, 4; data_index 0; roll_shift 14 402 | par_round4: h_ordering 4, 0, 1, 2, 3; data_index 5; roll_shift 6 403 | par_round4: h_ordering 3, 4, 0, 1, 2; data_index 12; roll_shift 9 404 | par_round4: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 12 405 | par_round4: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 9 406 | par_round4: h_ordering 0, 1, 2, 3, 4; data_index 9; roll_shift 12 407 | par_round4: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 5 408 | par_round4: h_ordering 3, 4, 0, 1, 2; data_index 10; roll_shift 15 409 | par_round4: h_ordering 2, 3, 4, 0, 1; data_index 14; roll_shift 8; 410 | 411 | // Parallel Round 5 412 | par_round5: h_ordering 1, 2, 3, 4, 0; data_index 12; roll_shift 8 413 | par_round5: h_ordering 0, 1, 2, 3, 4; data_index 15; roll_shift 5 414 | par_round5: h_ordering 4, 0, 1, 2, 3; data_index 10; roll_shift 12 415 | par_round5: h_ordering 3, 4, 0, 1, 2; data_index 4; roll_shift 9 416 | par_round5: h_ordering 2, 3, 4, 0, 1; data_index 1; roll_shift 12 417 | par_round5: h_ordering 1, 2, 3, 4, 0; data_index 5; roll_shift 5 418 | par_round5: h_ordering 0, 1, 2, 3, 4; data_index 8; roll_shift 14 419 | par_round5: h_ordering 4, 0, 1, 2, 3; data_index 7; roll_shift 6 420 | par_round5: h_ordering 3, 4, 0, 1, 2; data_index 6; roll_shift 8 421 | par_round5: h_ordering 2, 3, 4, 0, 1; data_index 2; roll_shift 13 422 | par_round5: h_ordering 1, 2, 3, 4, 0; data_index 13; roll_shift 6 423 | par_round5: h_ordering 0, 1, 2, 3, 4; data_index 14; roll_shift 5 424 | par_round5: h_ordering 4, 0, 1, 2, 3; data_index 0; roll_shift 15 425 | par_round5: h_ordering 3, 4, 0, 1, 2; data_index 3; roll_shift 13 426 | par_round5: h_ordering 2, 3, 4, 0, 1; data_index 9; roll_shift 11 427 | par_round5: h_ordering 1, 2, 3, 4, 0; data_index 11; roll_shift 11; 428 | ); 429 | } 430 | #[test] 431 | fn test_rmd160_compress() { 432 | let words = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 433 | let bytes: [u8; 64] = words 434 | .into_iter() 435 | .map(|x: u32| x.to_le_bytes()) 436 | .flatten() 437 | .collect::>() 438 | .try_into() 439 | .unwrap(); 440 | let w = super::H0.to_vec().clone(); 441 | let r = super::compress(&w, words.to_vec()); 442 | let mut r1 = super::H0.clone(); 443 | compress(&mut r1, &bytes); 444 | assert_eq!(r.to_vec(), r1); 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /src/circuits/rmd160.rs: -------------------------------------------------------------------------------- 1 | // The constraint system matrix for rmd160. 2 | 3 | use halo2_proofs::{ 4 | circuit::{Chip, Layouter, Region}, 5 | halo2curves::ff::PrimeField, 6 | plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, VirtualCells}, 7 | poly::Rotation, 8 | }; 9 | 10 | use crate::constant; 11 | use crate::host::rmd160::{RMD160Atomic, O, PO, PR, PROUNDS_OFFSET, R, ROUNDS_OFFSET}; 12 | use halo2_gate_generator::{ 13 | customized_circuits, customized_circuits_expand, item_count, table_item, value_for_assign, 14 | GateCell, Limb, 15 | }; 16 | use std::marker::PhantomData; 17 | 18 | use crate::utils::{field_to_u32, field_to_u64}; 19 | 20 | pub fn u32_to_limbs(v: u32) -> [F; 4] { 21 | let mut rem = v; 22 | let mut r = vec![]; 23 | for _ in 0..4 { 24 | r.append(&mut vec![F::from((rem % 256) as u64)]); 25 | rem = rem / 256; 26 | } 27 | r.try_into().unwrap() 28 | } 29 | 30 | pub fn limb_to_u32(limb: &Limb) -> [F; 4] { 31 | let a = field_to_u32(&limb.value); 32 | u32_to_limbs(a) 33 | } 34 | 35 | pub struct RMD160Chip { 36 | config: RMD160Config, 37 | _marker: PhantomData, 38 | } 39 | 40 | customized_circuits!(RoundGateConfig, 5, 7, 3, 0, |a| b 41 | | c 42 | | d 43 | | x 44 | | e 45 | | c_next 46 | | offset 47 | | h_sel 48 | | r_sel 49 | | w0 50 | | b0 51 | | c0 52 | | d0 53 | | r0 54 | | w1_h 55 | | w4_h 56 | | w1_r 57 | | nil 58 | | nil 59 | | wb 60 | | b1 61 | | c1 62 | | d1 63 | | r1 64 | | w1_l 65 | | w4_l 66 | | w1_rr 67 | | nil 68 | | nil 69 | | wc 70 | | b2 71 | | c2 72 | | d2 73 | | r2 74 | | a_next 75 | | w2b 76 | | nil 77 | | nil 78 | | nil 79 | | w1 80 | | b3 81 | | c3 82 | | d3 83 | | r3 84 | | nil 85 | | w2c 86 | | nil 87 | | nil 88 | | nil); 89 | 90 | /* All witness we need to fill the gate */ 91 | struct RoundWitness { 92 | r: u32, // atomic(b, c, d) 93 | w0: u32, // a + r + x + offset 94 | wb: F, // a + r + x + offset u64 95 | wc: u64, // wb - w0 96 | w1: u32, // w0 rorate_left w1_r 97 | w1_h: u32, //w1 >> w1_r 98 | w1_l: u32, //w1 % w1_r 99 | a_next: u32, // w1 + e 100 | w2b: F, // w1+e u64 101 | w2c: u64, // w2b - a_next 102 | w4_h: u32, // c >> w4_r 103 | w4_l: u32, // c % w4_r 104 | c_next: u32, // c rotate_left 10 105 | } 106 | 107 | fn get_witnesses( 108 | round: usize, 109 | rol: &[u32; 5], 110 | x: u32, 111 | shift: u32, 112 | offset: u32, 113 | pround: bool, 114 | ) -> RoundWitness { 115 | let f = if pround { 5 - round - 1 } else { round }; 116 | let r = u32::atomic(f, rol[1], rol[2], rol[3]); 117 | let w0 = r.wrapping_add(rol[0]).wrapping_add(x).wrapping_add(offset); 118 | let wb = 119 | F::from(r as u64) + F::from(rol[0] as u64) + F::from(x as u64) + F::from(offset as u64); 120 | let wc = (field_to_u64(&wb) - (w0 as u64)) >> 32; 121 | let w1 = w0.rotate_left(shift); 122 | let w1_h = w0 >> (32 - shift); 123 | let w1_l = w0 % (2u32.pow(32 - shift)); 124 | let a_next = w1.wrapping_add(rol[4]); 125 | let w2b = F::from(w1 as u64) + F::from(rol[4] as u64); 126 | let w2c = (field_to_u64(&w2b) - (a_next as u64)) >> 32; 127 | let w4_h = rol[2] >> 22; 128 | let w4_l = rol[2] % (2u32.pow(22)); 129 | let c_next = rol[2].rotate_left(10); 130 | 131 | //println!("round {}, shift {}, offset {} x {}", r, shift ,offset, x); 132 | //println!("w2c {}", w2c); 133 | 134 | RoundWitness { 135 | r, 136 | w0, 137 | wb, 138 | wc, 139 | w1, 140 | w1_h, 141 | w1_l, 142 | a_next, 143 | w2b, 144 | w2c, 145 | w4_h, 146 | w4_l, 147 | c_next, 148 | } 149 | } 150 | 151 | customized_circuits!(CompressSumConfig, 5, 7, 3, 0, |a| b1 152 | | c2 153 | | sum0 154 | | ca0 155 | | bnew 156 | | col6 157 | | col7 158 | | h_sel 159 | | r_sel 160 | | b 161 | | c1 162 | | d2 163 | | sum1 164 | | ca1 165 | | cnew 166 | | nil 167 | | nil 168 | | nil 169 | | nil 170 | | c 171 | | d1 172 | | e2 173 | | sum2 174 | | ca2 175 | | dnew 176 | | nil 177 | | nil 178 | | nil 179 | | nil 180 | | d 181 | | e1 182 | | a2 183 | | sum3 184 | | ca3 185 | | enew 186 | | nil 187 | | nil 188 | | nil 189 | | nil 190 | | e 191 | | a1 192 | | b2 193 | | sum4 194 | | ca4 195 | | anew 196 | | nil 197 | | nil 198 | | nil 199 | | nil); 200 | 201 | #[derive(Clone, Debug)] 202 | pub struct RMD160Config { 203 | compress_sum_config: CompressSumConfig, 204 | round_config: RoundGateConfig, 205 | } 206 | 207 | impl Chip for RMD160Chip { 208 | type Config = RMD160Config; 209 | type Loaded = (); 210 | 211 | fn config(&self) -> &Self::Config { 212 | &self.config 213 | } 214 | 215 | fn loaded(&self) -> &Self::Loaded { 216 | &() 217 | } 218 | } 219 | 220 | impl RMD160Chip { 221 | pub fn new(config: RMD160Config) -> Self { 222 | RMD160Chip { 223 | config, 224 | _marker: PhantomData, 225 | } 226 | } 227 | 228 | pub fn configure(cs: &mut ConstraintSystem) -> RMD160Config { 229 | let witness = [0; 7].map(|_| cs.advice_column()); 230 | let fixed = [0; 3].map(|_| cs.fixed_column()); 231 | let selector = []; 232 | witness.map(|x| cs.enable_equality(x)); 233 | 234 | let config = RMD160Config { 235 | compress_sum_config: CompressSumConfig::new(witness, fixed, selector), 236 | round_config: RoundGateConfig::new(witness, fixed, selector), 237 | }; 238 | 239 | cs.create_gate("sum with bound", |meta| { 240 | let r0 = config.round_config.get_expr(meta, RoundGateConfig::r0()); 241 | let r1 = config.round_config.get_expr(meta, RoundGateConfig::r1()); 242 | let r2 = config.round_config.get_expr(meta, RoundGateConfig::r2()); 243 | let r3 = config.round_config.get_expr(meta, RoundGateConfig::r3()); 244 | let sum_r = r0 245 | + (r1 + (r2 + r3 * F::from(1u64 << 8)) * F::from(1u64 << 8)) * F::from(1u64 << 8); 246 | let w0 = config.round_config.get_expr(meta, RoundGateConfig::w0()); 247 | let wb = config.round_config.get_expr(meta, RoundGateConfig::wb()); 248 | let wc = config.round_config.get_expr(meta, RoundGateConfig::wc()); 249 | let a = config.round_config.get_expr(meta, RoundGateConfig::a()); 250 | let x = config.round_config.get_expr(meta, RoundGateConfig::x()); 251 | let offset = config 252 | .round_config 253 | .get_expr(meta, RoundGateConfig::offset()); 254 | let hsel = config.round_config.get_expr(meta, RoundGateConfig::h_sel()); 255 | vec![ 256 | (wb.clone() - sum_r - a - x - offset) * hsel.clone(), 257 | //(wc.clone()*(wc.clone() - constant!(F::ONE))) * hsel.clone(), 258 | (w0 + wc * F::from(1u64 << 32) - wb) * hsel, 259 | ] 260 | }); 261 | 262 | cs.create_gate("sum with w1 rol4", |meta| { 263 | let a_next = config 264 | .round_config 265 | .get_expr(meta, RoundGateConfig::a_next()); 266 | let hsel = config.round_config.get_expr(meta, RoundGateConfig::h_sel()); 267 | let w1 = config.round_config.get_expr(meta, RoundGateConfig::w1()); 268 | let w2b = config.round_config.get_expr(meta, RoundGateConfig::w2b()); 269 | 270 | let w2c = config.round_config.get_expr(meta, RoundGateConfig::w2c()); 271 | let e = config.round_config.get_expr(meta, RoundGateConfig::e()); 272 | vec![ 273 | (w2b.clone() - w1 - e) * hsel.clone(), 274 | (w2c.clone() * (w2c.clone() - constant!(F::ONE))) * hsel.clone(), 275 | (a_next + w2c * F::from(1u64 << 32) - w2b) * hsel, 276 | ] 277 | }); 278 | 279 | cs.create_gate("limbs sum", |meta| { 280 | let hsel = config.round_config.get_expr(meta, RoundGateConfig::h_sel()); 281 | 282 | let b = config.round_config.get_expr(meta, RoundGateConfig::b()); 283 | let b0 = config.round_config.get_expr(meta, RoundGateConfig::b0()); 284 | let b1 = config.round_config.get_expr(meta, RoundGateConfig::b1()); 285 | let b2 = config.round_config.get_expr(meta, RoundGateConfig::b2()); 286 | let b3 = config.round_config.get_expr(meta, RoundGateConfig::b3()); 287 | let sum_b = b0 288 | + (b1 + (b2 + b3 * F::from(1u64 << 8)) * F::from(1u64 << 8)) * F::from(1u64 << 8); 289 | 290 | let c = config.round_config.get_expr(meta, RoundGateConfig::c()); 291 | let c0 = config.round_config.get_expr(meta, RoundGateConfig::c0()); 292 | let c1 = config.round_config.get_expr(meta, RoundGateConfig::c1()); 293 | let c2 = config.round_config.get_expr(meta, RoundGateConfig::c2()); 294 | let c3 = config.round_config.get_expr(meta, RoundGateConfig::c3()); 295 | let sum_c = c0 296 | + (c1 + (c2 + c3 * F::from(1u64 << 8)) * F::from(1u64 << 8)) * F::from(1u64 << 8); 297 | 298 | let d = config.round_config.get_expr(meta, RoundGateConfig::d()); 299 | let d0 = config.round_config.get_expr(meta, RoundGateConfig::d0()); 300 | let d1 = config.round_config.get_expr(meta, RoundGateConfig::d1()); 301 | let d2 = config.round_config.get_expr(meta, RoundGateConfig::d2()); 302 | let d3 = config.round_config.get_expr(meta, RoundGateConfig::d3()); 303 | let sum_d = d0 304 | + (d1 + (d2 + d3 * F::from(1u64 << 8)) * F::from(1u64 << 8)) * F::from(1u64 << 8); 305 | 306 | vec![ 307 | (sum_b - b) * hsel.clone(), 308 | (sum_c - c) * hsel.clone(), 309 | (sum_d - d) * hsel.clone(), 310 | ] 311 | }); 312 | 313 | cs.create_gate("c rotate", |meta| { 314 | let hsel = config.round_config.get_expr(meta, RoundGateConfig::h_sel()); 315 | 316 | let c = config.round_config.get_expr(meta, RoundGateConfig::c()); 317 | let c_next = config 318 | .round_config 319 | .get_expr(meta, RoundGateConfig::c_next()); 320 | let w4l = config.round_config.get_expr(meta, RoundGateConfig::w4_l()); 321 | let w4h = config.round_config.get_expr(meta, RoundGateConfig::w4_h()); 322 | 323 | vec![ 324 | (w4h.clone() * constant!(F::from(1u64 << 22)) + w4l.clone() - c.clone()) 325 | * hsel.clone(), 326 | (w4l * constant!(F::from(1u64 << 10)) + w4h - c_next.clone()) * hsel.clone(), 327 | ] 328 | }); 329 | 330 | cs.create_gate("w0 rotate", |meta| { 331 | let hsel = config.round_config.get_expr(meta, RoundGateConfig::h_sel()); 332 | let w0 = config.round_config.get_expr(meta, RoundGateConfig::w0()); 333 | let w1 = config.round_config.get_expr(meta, RoundGateConfig::w1()); 334 | let w1l = config.round_config.get_expr(meta, RoundGateConfig::w1_l()); 335 | let w1h = config.round_config.get_expr(meta, RoundGateConfig::w1_h()); 336 | let shift = config.round_config.get_expr(meta, RoundGateConfig::w1_r()); 337 | let shift2 = config.round_config.get_expr(meta, RoundGateConfig::w1_rr()); 338 | 339 | vec![ 340 | (w1h.clone() * shift2.clone() + w1l.clone() - w0) * hsel.clone(), 341 | (w1l * shift + w1h - w1) * hsel.clone(), 342 | ] 343 | }); 344 | 345 | config 346 | } 347 | 348 | fn assign_next( 349 | &self, 350 | region: &mut Region, 351 | start_offset: usize, 352 | previous: &[Limb; 5], 353 | input: &Limb, 354 | round: usize, 355 | index: usize, 356 | shift: &[[u32; 16]; 5], 357 | offset: &[u32; 5], 358 | pround: bool, 359 | ) -> Result<[Limb; 5], Error> { 360 | //println!("rol: {:?}", previous.clone().map(|x| cell_to_u32(&x))); 361 | self.config.round_config.bind_cell( 362 | region, 363 | start_offset, 364 | &RoundGateConfig::a(), 365 | &previous[0], 366 | )?; 367 | let b = self.config.round_config.bind_cell( 368 | region, 369 | start_offset, 370 | &RoundGateConfig::b(), 371 | &previous[1], 372 | )?; 373 | self.config.round_config.bind_cell( 374 | region, 375 | start_offset, 376 | &RoundGateConfig::c(), 377 | &previous[2], 378 | )?; 379 | let d = self.config.round_config.bind_cell( 380 | region, 381 | start_offset, 382 | &RoundGateConfig::d(), 383 | &previous[3], 384 | )?; 385 | let e = self.config.round_config.bind_cell( 386 | region, 387 | start_offset, 388 | &RoundGateConfig::e(), 389 | &previous[4], 390 | )?; 391 | 392 | self.config 393 | .round_config 394 | .bind_cell(region, start_offset, &RoundGateConfig::x(), input)?; 395 | 396 | self.config.round_config.assign_cell( 397 | region, 398 | start_offset, 399 | &RoundGateConfig::w1_r(), 400 | F::from(1u64 << shift[round][index]), 401 | )?; 402 | self.config.round_config.assign_cell( 403 | region, 404 | start_offset, 405 | &RoundGateConfig::w1_rr(), 406 | F::from(1u64 << (32 - shift[round][index])), 407 | )?; 408 | 409 | let blimbs = limb_to_u32(&previous[1]); 410 | self.config.round_config.assign_cell( 411 | region, 412 | start_offset, 413 | &RoundGateConfig::b0(), 414 | blimbs[0], 415 | )?; 416 | self.config.round_config.assign_cell( 417 | region, 418 | start_offset, 419 | &RoundGateConfig::b1(), 420 | blimbs[1], 421 | )?; 422 | self.config.round_config.assign_cell( 423 | region, 424 | start_offset, 425 | &RoundGateConfig::b2(), 426 | blimbs[2], 427 | )?; 428 | self.config.round_config.assign_cell( 429 | region, 430 | start_offset, 431 | &RoundGateConfig::b3(), 432 | blimbs[3], 433 | )?; 434 | 435 | let climbs = limb_to_u32(&previous[2]); 436 | self.config.round_config.assign_cell( 437 | region, 438 | start_offset, 439 | &RoundGateConfig::c0(), 440 | climbs[0], 441 | )?; 442 | self.config.round_config.assign_cell( 443 | region, 444 | start_offset, 445 | &RoundGateConfig::c1(), 446 | climbs[1], 447 | )?; 448 | self.config.round_config.assign_cell( 449 | region, 450 | start_offset, 451 | &RoundGateConfig::c2(), 452 | climbs[2], 453 | )?; 454 | self.config.round_config.assign_cell( 455 | region, 456 | start_offset, 457 | &RoundGateConfig::c3(), 458 | climbs[3], 459 | )?; 460 | 461 | let dlimbs = limb_to_u32(&previous[3]); 462 | self.config.round_config.assign_cell( 463 | region, 464 | start_offset, 465 | &RoundGateConfig::d0(), 466 | dlimbs[0], 467 | )?; 468 | self.config.round_config.assign_cell( 469 | region, 470 | start_offset, 471 | &RoundGateConfig::d1(), 472 | dlimbs[1], 473 | )?; 474 | self.config.round_config.assign_cell( 475 | region, 476 | start_offset, 477 | &RoundGateConfig::d2(), 478 | dlimbs[2], 479 | )?; 480 | self.config.round_config.assign_cell( 481 | region, 482 | start_offset, 483 | &RoundGateConfig::d3(), 484 | dlimbs[3], 485 | )?; 486 | 487 | let rol = previous 488 | .into_iter() 489 | .map(|c| field_to_u32(&c.value)) 490 | .collect::>() 491 | .try_into() 492 | .unwrap(); 493 | 494 | let witness = get_witnesses( 495 | round, 496 | &rol, 497 | field_to_u32(&input.value), 498 | shift[round][index], 499 | offset[round], 500 | pround, 501 | ); 502 | //self.assign_cell(region, start_offset, RoundGateConfig::r(), F::from(witness.r as u64)); 503 | // 504 | self.config.round_config.assign_cell( 505 | region, 506 | start_offset, 507 | &RoundGateConfig::offset(), 508 | F::from(offset[round] as u64), 509 | )?; 510 | let rlimbs = u32_to_limbs(witness.r); 511 | 512 | let mut sum_r = rlimbs[0]; 513 | for i in 1..4 { 514 | sum_r = sum_r + rlimbs[i] * F::from(1u64 << (8 * i)); 515 | } 516 | 517 | assert!(sum_r == F::from(witness.r as u64)); 518 | 519 | assert!( 520 | witness.w2b 521 | == F::from(witness.w1 as u64) + F::from(field_to_u32(&previous[4].value) as u64) 522 | ); 523 | assert!( 524 | witness.wb 525 | == F::from(witness.r as u64) 526 | + F::from(field_to_u32(&previous[0].value) as u64) 527 | + F::from(field_to_u32(&input.value) as u64) 528 | + F::from(offset[round] as u64) 529 | ); 530 | 531 | self.config.round_config.assign_cell( 532 | region, 533 | start_offset, 534 | &RoundGateConfig::r0(), 535 | rlimbs[0], 536 | )?; 537 | self.config.round_config.assign_cell( 538 | region, 539 | start_offset, 540 | &RoundGateConfig::r1(), 541 | rlimbs[1], 542 | )?; 543 | self.config.round_config.assign_cell( 544 | region, 545 | start_offset, 546 | &RoundGateConfig::r2(), 547 | rlimbs[2], 548 | )?; 549 | self.config.round_config.assign_cell( 550 | region, 551 | start_offset, 552 | &RoundGateConfig::r3(), 553 | rlimbs[3], 554 | )?; 555 | 556 | self.config.round_config.assign_cell( 557 | region, 558 | start_offset, 559 | &RoundGateConfig::w0(), 560 | F::from(witness.w0 as u64), 561 | )?; 562 | self.config.round_config.assign_cell( 563 | region, 564 | start_offset, 565 | &RoundGateConfig::wb(), 566 | witness.wb, 567 | )?; 568 | self.config.round_config.assign_cell( 569 | region, 570 | start_offset, 571 | &RoundGateConfig::wc(), 572 | F::from(witness.wc as u64), 573 | )?; 574 | self.config.round_config.assign_cell( 575 | region, 576 | start_offset, 577 | &RoundGateConfig::w1(), 578 | F::from(witness.w1 as u64), 579 | )?; 580 | self.config.round_config.assign_cell( 581 | region, 582 | start_offset, 583 | &RoundGateConfig::w1_h(), 584 | F::from(witness.w1_h as u64), 585 | )?; 586 | self.config.round_config.assign_cell( 587 | region, 588 | start_offset, 589 | &RoundGateConfig::w1_l(), 590 | F::from(witness.w1_l as u64), 591 | )?; 592 | self.config.round_config.assign_cell( 593 | region, 594 | start_offset, 595 | &RoundGateConfig::w4_h(), 596 | F::from(witness.w4_h as u64), 597 | )?; 598 | self.config.round_config.assign_cell( 599 | region, 600 | start_offset, 601 | &RoundGateConfig::w4_l(), 602 | F::from(witness.w4_l as u64), 603 | )?; 604 | self.config.round_config.assign_cell( 605 | region, 606 | start_offset, 607 | &RoundGateConfig::w2b(), 608 | witness.w2b, 609 | )?; 610 | self.config.round_config.assign_cell( 611 | region, 612 | start_offset, 613 | &RoundGateConfig::w2c(), 614 | F::from(witness.w2c as u64), 615 | )?; 616 | self.config.round_config.assign_cell( 617 | region, 618 | start_offset, 619 | &RoundGateConfig::h_sel(), 620 | F::ONE, 621 | )?; 622 | let a = self.config.round_config.assign_cell( 623 | region, 624 | start_offset, 625 | &RoundGateConfig::a_next(), 626 | F::from(witness.a_next as u64), 627 | )?; 628 | let c = self.config.round_config.assign_cell( 629 | region, 630 | start_offset, 631 | &RoundGateConfig::c_next(), 632 | F::from(witness.c_next as u64), 633 | )?; 634 | Ok([e, a, b, c, d]) 635 | } 636 | 637 | fn rotate_inputs(&self, inputs: &[Limb; 16], round_shift: [usize; 16]) -> [Limb; 16] { 638 | round_shift.map(|i| inputs[i].clone()) 639 | } 640 | 641 | pub fn assign_compress( 642 | &self, 643 | region: &mut Region, 644 | start_offset: usize, 645 | r0: &[Limb; 5], 646 | r1: &[Limb; 5], 647 | r2: &[Limb; 5], 648 | ) -> Result<[Limb; 5], Error> { 649 | self.config.round_config.bind_cell( 650 | region, 651 | start_offset, 652 | &CompressSumConfig::a(), 653 | &r0[0], 654 | )?; 655 | self.config.compress_sum_config.bind_cell( 656 | region, 657 | start_offset, 658 | &CompressSumConfig::b(), 659 | &r0[1], 660 | )?; 661 | self.config.compress_sum_config.bind_cell( 662 | region, 663 | start_offset, 664 | &CompressSumConfig::c(), 665 | &r0[2], 666 | )?; 667 | self.config.compress_sum_config.bind_cell( 668 | region, 669 | start_offset, 670 | &CompressSumConfig::d(), 671 | &r0[3], 672 | )?; 673 | self.config.compress_sum_config.bind_cell( 674 | region, 675 | start_offset, 676 | &CompressSumConfig::e(), 677 | &r0[4], 678 | )?; 679 | self.config.compress_sum_config.bind_cell( 680 | region, 681 | start_offset, 682 | &CompressSumConfig::a1(), 683 | &r1[0], 684 | )?; 685 | self.config.compress_sum_config.bind_cell( 686 | region, 687 | start_offset, 688 | &CompressSumConfig::b1(), 689 | &r1[1], 690 | )?; 691 | self.config.compress_sum_config.bind_cell( 692 | region, 693 | start_offset, 694 | &CompressSumConfig::c1(), 695 | &r1[2], 696 | )?; 697 | self.config.compress_sum_config.bind_cell( 698 | region, 699 | start_offset, 700 | &CompressSumConfig::d1(), 701 | &r1[3], 702 | )?; 703 | self.config.compress_sum_config.bind_cell( 704 | region, 705 | start_offset, 706 | &CompressSumConfig::e1(), 707 | &r1[4], 708 | )?; 709 | self.config.compress_sum_config.bind_cell( 710 | region, 711 | start_offset, 712 | &CompressSumConfig::a2(), 713 | &r2[0], 714 | )?; 715 | self.config.compress_sum_config.bind_cell( 716 | region, 717 | start_offset, 718 | &CompressSumConfig::b2(), 719 | &r2[1], 720 | )?; 721 | self.config.compress_sum_config.bind_cell( 722 | region, 723 | start_offset, 724 | &CompressSumConfig::c2(), 725 | &r2[2], 726 | )?; 727 | self.config.compress_sum_config.bind_cell( 728 | region, 729 | start_offset, 730 | &CompressSumConfig::d2(), 731 | &r2[3], 732 | )?; 733 | self.config.compress_sum_config.bind_cell( 734 | region, 735 | start_offset, 736 | &CompressSumConfig::e2(), 737 | &r2[4], 738 | )?; 739 | 740 | let anew = { 741 | let anew = field_to_u32(&r0[0].value) 742 | .wrapping_add(field_to_u32(&r1[1].value)) 743 | .wrapping_add(field_to_u32(&r2[2].value)); 744 | let sum0 = r0[0].value + r1[1].value + r2[2].value; 745 | let ca0 = (field_to_u64(&sum0) - anew as u64) >> 32; 746 | self.config.compress_sum_config.assign_cell( 747 | region, 748 | start_offset, 749 | &CompressSumConfig::sum0(), 750 | sum0, 751 | )?; 752 | self.config.compress_sum_config.assign_cell( 753 | region, 754 | start_offset, 755 | &CompressSumConfig::ca0(), 756 | F::from(ca0), 757 | )?; 758 | self.config.compress_sum_config.assign_cell( 759 | region, 760 | start_offset, 761 | &CompressSumConfig::anew(), 762 | F::from(anew as u64), 763 | )? 764 | }; 765 | 766 | let bnew = { 767 | let bnew = field_to_u32(&r0[1].value) 768 | .wrapping_add(field_to_u32(&r1[2].value)) 769 | .wrapping_add(field_to_u32(&r2[3].value)); 770 | let sum1 = r0[1].value + r1[2].value + r2[3].value; 771 | let ca1 = (field_to_u64(&sum1) - bnew as u64) >> 32; 772 | self.config.compress_sum_config.assign_cell( 773 | region, 774 | start_offset, 775 | &CompressSumConfig::sum1(), 776 | sum1, 777 | )?; 778 | self.config.compress_sum_config.assign_cell( 779 | region, 780 | start_offset, 781 | &CompressSumConfig::ca1(), 782 | F::from(ca1), 783 | )?; 784 | self.config.compress_sum_config.assign_cell( 785 | region, 786 | start_offset, 787 | &CompressSumConfig::bnew(), 788 | F::from(bnew as u64), 789 | )? 790 | }; 791 | 792 | let cnew = { 793 | let cnew = field_to_u32(&r0[2].value) 794 | .wrapping_add(field_to_u32(&r1[3].value)) 795 | .wrapping_add(field_to_u32(&r2[4].value)); 796 | let sum2 = r0[2].value + r1[3].value + r2[4].value; 797 | let ca2 = (field_to_u64(&sum2) - cnew as u64) >> 32; 798 | self.config.compress_sum_config.assign_cell( 799 | region, 800 | start_offset, 801 | &CompressSumConfig::sum2(), 802 | sum2, 803 | )?; 804 | self.config.compress_sum_config.assign_cell( 805 | region, 806 | start_offset, 807 | &CompressSumConfig::ca0(), 808 | F::from(ca2), 809 | )?; 810 | self.config.compress_sum_config.assign_cell( 811 | region, 812 | start_offset, 813 | &CompressSumConfig::cnew(), 814 | F::from(cnew as u64), 815 | )? 816 | }; 817 | 818 | let dnew = { 819 | let dnew = field_to_u32(&r0[3].value) 820 | .wrapping_add(field_to_u32(&r1[4].value)) 821 | .wrapping_add(field_to_u32(&r2[0].value)); 822 | let sum3 = r0[3].value + r1[4].value + r2[0].value; 823 | let ca3 = (field_to_u64(&sum3) - dnew as u64) >> 32; 824 | self.config.compress_sum_config.assign_cell( 825 | region, 826 | start_offset, 827 | &CompressSumConfig::sum3(), 828 | sum3, 829 | )?; 830 | self.config.compress_sum_config.assign_cell( 831 | region, 832 | start_offset, 833 | &CompressSumConfig::ca3(), 834 | F::from(ca3), 835 | )?; 836 | self.config.compress_sum_config.assign_cell( 837 | region, 838 | start_offset, 839 | &CompressSumConfig::dnew(), 840 | F::from(dnew as u64), 841 | )? 842 | }; 843 | 844 | let enew = { 845 | let enew = field_to_u32(&r0[4].value) 846 | .wrapping_add(field_to_u32(&r1[0].value)) 847 | .wrapping_add(field_to_u32(&r2[1].value)); 848 | let sum4 = r0[4].value + r1[0].value + r2[1].value; 849 | let ca4 = (field_to_u64(&sum4) - enew as u64) >> 32; 850 | self.config.compress_sum_config.assign_cell( 851 | region, 852 | start_offset, 853 | &CompressSumConfig::sum4(), 854 | sum4, 855 | )?; 856 | self.config.compress_sum_config.assign_cell( 857 | region, 858 | start_offset, 859 | &CompressSumConfig::ca4(), 860 | F::from(ca4), 861 | )?; 862 | self.config.compress_sum_config.assign_cell( 863 | region, 864 | start_offset, 865 | &CompressSumConfig::enew(), 866 | F::from(enew as u64), 867 | )? 868 | }; 869 | 870 | Ok([anew, bnew, cnew, dnew, enew]) 871 | } 872 | 873 | pub fn assign_content( 874 | &self, 875 | layouter: &mut impl Layouter, 876 | start_buf: &[Limb; 5], 877 | inputs: &[Limb; 16], 878 | ) -> Result<[Limb; 5], Error> { 879 | let r = layouter.assign_region( 880 | || "leaf layer", 881 | |mut region| { 882 | let mut r1 = start_buf.clone(); 883 | let mut start_offset = 0; 884 | for (round, o) in O.iter().copied().enumerate() { 885 | for index in 0..16 { 886 | r1 = self.assign_next( 887 | &mut region, 888 | start_offset, 889 | &r1, 890 | &self.rotate_inputs(inputs, o)[index], 891 | round, 892 | index, 893 | &R, 894 | &ROUNDS_OFFSET, 895 | false, 896 | )?; 897 | start_offset += 5; 898 | } 899 | } 900 | /* 901 | println!("{} {} {} {} {}", 902 | cell_to_u32(&r1[0]), 903 | cell_to_u32(&r1[1]), 904 | cell_to_u32(&r1[2]), 905 | cell_to_u32(&r1[3]), 906 | cell_to_u32(&r1[4]), 907 | ); 908 | */ 909 | 910 | let mut r2 = start_buf.clone(); 911 | for (round, po) in PO.iter().copied().enumerate() { 912 | for index in 0..16 { 913 | r2 = self.assign_next( 914 | &mut region, 915 | start_offset, 916 | &r2, 917 | &self.rotate_inputs(inputs, po)[index], 918 | round, 919 | index, 920 | &PR, 921 | &PROUNDS_OFFSET, 922 | true, 923 | )?; 924 | start_offset += 5; 925 | } 926 | } 927 | self.assign_compress(&mut region, start_offset, start_buf, &r1, &r2) 928 | }, 929 | )?; 930 | Ok(r) 931 | } 932 | } 933 | 934 | #[cfg(test)] 935 | mod tests { 936 | use halo2_proofs::dev::MockProver; 937 | use halo2_proofs::halo2curves::bn256::Fr; 938 | 939 | use halo2_proofs::{ 940 | circuit::{Chip, Layouter, SimpleFloorPlanner}, 941 | plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, 942 | }; 943 | 944 | use super::RMD160Chip; 945 | use super::RMD160Config; 946 | use crate::host::rmd160::H0; 947 | use crate::utils::field_to_u32; 948 | use crate::value_for_assign; 949 | use halo2_gate_generator::Limb; 950 | 951 | #[derive(Clone, Debug)] 952 | pub struct HelperChipConfig { 953 | limb: Column, 954 | } 955 | 956 | #[derive(Clone, Debug)] 957 | pub struct HelperChip { 958 | config: HelperChipConfig, 959 | } 960 | 961 | impl Chip for HelperChip { 962 | type Config = HelperChipConfig; 963 | type Loaded = (); 964 | 965 | fn config(&self) -> &Self::Config { 966 | &self.config 967 | } 968 | 969 | fn loaded(&self) -> &Self::Loaded { 970 | &() 971 | } 972 | } 973 | 974 | impl HelperChip { 975 | fn new(config: HelperChipConfig) -> Self { 976 | HelperChip { config } 977 | } 978 | 979 | fn configure(cs: &mut ConstraintSystem) -> HelperChipConfig { 980 | let limb = cs.advice_column(); 981 | cs.enable_equality(limb); 982 | HelperChipConfig { limb } 983 | } 984 | 985 | fn assign_w( 986 | &self, 987 | layouter: &mut impl Layouter, 988 | inputs: &[u32; 5], 989 | offset: usize, 990 | ) -> Result<[Limb; 5], Error> { 991 | layouter.assign_region( 992 | || "leaf layer", 993 | |mut region| { 994 | let mut r = vec![]; 995 | for round in 0..5 { 996 | let cell = region.assign_advice( 997 | || format!("assign w"), 998 | self.config.limb, 999 | offset + round, 1000 | || value_for_assign!(Fr::from(inputs[round] as u64)), 1001 | )?; 1002 | r.push(Limb::new(Some(cell), Fr::from(inputs[round] as u64))) 1003 | } 1004 | Ok(r.try_into().unwrap()) 1005 | }, 1006 | ) 1007 | } 1008 | 1009 | fn assign_inputs( 1010 | &self, 1011 | layouter: &mut impl Layouter, 1012 | inputs: &[Fr; 16], 1013 | offset: usize, 1014 | ) -> Result<[Limb; 16], Error> { 1015 | layouter.assign_region( 1016 | || "leaf layer", 1017 | |mut region| { 1018 | let mut r = vec![]; 1019 | for i in 0..16 { 1020 | let cell = region.assign_advice( 1021 | || format!("assign input"), 1022 | self.config.limb, 1023 | offset + i, 1024 | || value_for_assign!(inputs[i]), 1025 | )?; 1026 | r.push(Limb::new(Some(cell), inputs[i])); 1027 | } 1028 | Ok(r.try_into().unwrap()) 1029 | }, 1030 | ) 1031 | } 1032 | } 1033 | 1034 | #[derive(Clone, Debug, Default)] 1035 | struct RMD160Circuit { 1036 | inputs: [Fr; 16], 1037 | } 1038 | 1039 | #[derive(Clone, Debug)] 1040 | struct TestConfig { 1041 | rmd160config: RMD160Config, 1042 | helperconfig: HelperChipConfig, 1043 | } 1044 | 1045 | impl Circuit for RMD160Circuit { 1046 | type Config = TestConfig; 1047 | type FloorPlanner = SimpleFloorPlanner; 1048 | 1049 | fn without_witnesses(&self) -> Self { 1050 | Self::default() 1051 | } 1052 | 1053 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1054 | Self::Config { 1055 | rmd160config: RMD160Chip::::configure(meta), 1056 | helperconfig: HelperChip::configure(meta), 1057 | } 1058 | } 1059 | 1060 | fn synthesize( 1061 | &self, 1062 | config: Self::Config, 1063 | mut layouter: impl Layouter, 1064 | ) -> Result<(), Error> { 1065 | let rmd160chip = RMD160Chip::::new(config.clone().rmd160config); 1066 | let helperchip = HelperChip::new(config.clone().helperconfig); 1067 | let w = helperchip.assign_w(&mut layouter, &H0, 0)?; 1068 | let input = helperchip.assign_inputs(&mut layouter, &self.inputs, 0)?; 1069 | let r = rmd160chip.assign_content(&mut layouter, &w, &input)?; 1070 | println!( 1071 | "{} {} {} {} {}", 1072 | field_to_u32(&r[0].value), 1073 | field_to_u32(&r[1].value), 1074 | field_to_u32(&r[2].value), 1075 | field_to_u32(&r[3].value), 1076 | field_to_u32(&r[4].value), 1077 | ); 1078 | Ok(()) 1079 | } 1080 | } 1081 | 1082 | #[test] 1083 | fn test_rmd160_circuit() { 1084 | let test_circuit = RMD160Circuit { 1085 | inputs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 1086 | .map(|x| Fr::from(x as u64)), 1087 | }; 1088 | let prover = MockProver::run(16, &test_circuit, vec![]).unwrap(); 1089 | assert_eq!(prover.verify(), Ok(())); 1090 | } 1091 | } 1092 | -------------------------------------------------------------------------------- /src/circuits/modexp.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::{bn_to_field, field_to_bn}; 2 | use halo2_gate_generator::Limb; 3 | use halo2_proofs::halo2curves::ff::{FromUniformBytes, PrimeField}; 4 | 5 | use crate::circuits::CommonGateConfig; 6 | 7 | use crate::circuits::range::{RangeCheckChip, RangeCheckConfig}; 8 | 9 | use std::ops::{Div, Mul}; 10 | 11 | use halo2_proofs::{ 12 | circuit::{Chip, Region}, 13 | plonk::{ConstraintSystem, Error}, 14 | }; 15 | use num_bigint::BigUint; 16 | use std::marker::PhantomData; 17 | 18 | pub struct ModExpChip { 19 | config: CommonGateConfig, 20 | _marker: PhantomData, 21 | } 22 | 23 | #[derive(Clone, Debug)] 24 | pub struct Number { 25 | pub limbs: [Limb; 4], 26 | } 27 | 28 | impl> Number { 29 | pub fn add(&self, n: &Self) -> Self { 30 | Number { 31 | limbs: [ 32 | Limb::new(None, self.limbs[0].value + n.limbs[0].value), 33 | Limb::new(None, self.limbs[1].value + n.limbs[1].value), 34 | Limb::new(None, self.limbs[2].value + n.limbs[2].value), 35 | Limb::new(None, self.limbs[3].value + n.limbs[3].value), 36 | ], 37 | } 38 | } 39 | pub fn from_bn(bn: &BigUint) -> Self { 40 | let limb0 = bn.modpow(&BigUint::from(1u128), &BigUint::from(1u128 << 108)); 41 | let limb1 = (bn - limb0.clone()) 42 | .div(BigUint::from(1u128 << 108)) 43 | .modpow(&BigUint::from(1u128), &BigUint::from(1u128 << 108)); 44 | let limb2 = bn 45 | .div(BigUint::from(1u128 << 108)) 46 | .div(BigUint::from(1u128 << 108)); 47 | let native = bn % (field_to_bn(&(-F::ONE)) + BigUint::from(1u128)); 48 | Number { 49 | limbs: [ 50 | Limb::new(None, bn_to_field(&limb0)), 51 | Limb::new(None, bn_to_field(&limb1)), 52 | Limb::new(None, bn_to_field(&limb2)), 53 | Limb::new(None, bn_to_field(&native)), 54 | ], 55 | } 56 | } 57 | pub fn to_bn(&self) -> BigUint { 58 | let limb0 = field_to_bn(&self.limbs[0].value); 59 | let limb1 = field_to_bn(&self.limbs[1].value); 60 | let limb2 = field_to_bn(&self.limbs[2].value); 61 | (limb2 * BigUint::from(1u128 << 108) + limb1) * BigUint::from(1u128 << 108) + limb0 62 | } 63 | } 64 | 65 | impl Chip for ModExpChip { 66 | type Config = CommonGateConfig; 67 | type Loaded = (); 68 | 69 | fn config(&self) -> &Self::Config { 70 | &self.config 71 | } 72 | 73 | fn loaded(&self) -> &Self::Loaded { 74 | &() 75 | } 76 | } 77 | 78 | impl> ModExpChip { 79 | pub fn new(config: CommonGateConfig) -> Self { 80 | ModExpChip { 81 | config, 82 | _marker: PhantomData, 83 | } 84 | } 85 | 86 | pub fn configure( 87 | cs: &mut ConstraintSystem, 88 | range_check_config: &RangeCheckConfig, 89 | ) -> CommonGateConfig { 90 | CommonGateConfig::configure(cs, range_check_config) 91 | } 92 | 93 | pub fn assign_constant( 94 | &self, 95 | region: &mut Region, 96 | range_check_chip: &mut RangeCheckChip, 97 | offset: &mut usize, 98 | number: Number, 99 | limbbound: u64, 100 | ) -> Result, Error> { 101 | let mut limbs = vec![]; 102 | for i in 0..4 { 103 | let l = self.config.assign_line( 104 | region, 105 | range_check_chip, 106 | offset, 107 | [Some(number.limbs[i].clone()), None, None, None, None, None], 108 | [ 109 | None, 110 | None, 111 | None, 112 | None, 113 | None, 114 | None, 115 | Some(number.limbs[i].value), 116 | None, 117 | None, 118 | ], 119 | limbbound, 120 | )?; 121 | limbs.push(l[0].clone()) 122 | } 123 | Ok(Number { 124 | limbs: limbs.try_into().unwrap(), 125 | }) 126 | } 127 | 128 | pub fn assign_number( 129 | &self, 130 | _region: &mut Region, 131 | _range_check_chip: &mut RangeCheckChip, 132 | _offset: &mut usize, 133 | number: Number, 134 | ) -> Result, Error> { 135 | Ok(number) 136 | } 137 | 138 | /// check whether lhs is less or equal to rhs 139 | fn le_limb( 140 | &self, 141 | region: &mut Region, 142 | range_check_chip: &mut RangeCheckChip, 143 | offset: &mut usize, 144 | lhs: &Limb, 145 | rhs: &Limb, 146 | ) -> Result, Error> { 147 | let bn_rhs = field_to_bn(&rhs.value); 148 | let bn_lhs = field_to_bn(&lhs.value); 149 | let (diff_true, diff_false) = ( 150 | Limb::new(None, bn_to_field::(&bn_rhs) - bn_to_field::(&bn_lhs)), 151 | Limb::new( 152 | None, 153 | bn_to_field::(&bn_lhs) - bn_to_field::(&bn_rhs) - F::ONE, 154 | ), 155 | ); 156 | let (abs, res) = if bn_rhs >= bn_lhs { 157 | ( 158 | Limb::new(None, bn_to_field::(&(bn_rhs - bn_lhs))), 159 | Limb::new(None, F::ONE), 160 | ) 161 | } else { 162 | ( 163 | Limb::new( 164 | None, 165 | bn_to_field::(&(bn_lhs - bn_rhs - BigUint::from(1u64))), 166 | ), 167 | Limb::new(None, F::ZERO), 168 | ) 169 | }; 170 | let true_limb = self.config.assign_line( 171 | region, 172 | range_check_chip, 173 | offset, 174 | [ 175 | Some(diff_true.clone()), 176 | Some(rhs.clone()), 177 | Some(lhs.clone()), 178 | None, 179 | None, 180 | None, 181 | ], 182 | [ 183 | Some(-F::ONE), 184 | Some(F::ONE), 185 | Some(-F::ONE), 186 | None, 187 | None, 188 | None, 189 | None, 190 | None, 191 | None, 192 | ], 193 | 0, 194 | )?[0] 195 | .clone(); 196 | 197 | let false_limb = self.config.assign_line( 198 | region, 199 | range_check_chip, 200 | offset, 201 | [ 202 | Some(diff_false.clone()), 203 | Some(rhs.clone()), 204 | Some(lhs.clone()), 205 | None, 206 | None, 207 | None, 208 | ], 209 | [ 210 | Some(-F::ONE), 211 | Some(-F::ONE), 212 | Some(F::ONE), 213 | None, 214 | None, 215 | None, 216 | None, 217 | None, 218 | Some(-F::ONE), 219 | ], 220 | 0, 221 | )?[0] 222 | .clone(); 223 | 224 | //res (abs - true_limb) == 0 225 | let [abs, _, _, res]: [Limb<_>; 4] = self 226 | .config 227 | .assign_line( 228 | region, 229 | range_check_chip, 230 | offset, 231 | [ 232 | Some(abs.clone()), 233 | Some(res.clone()), 234 | Some(true_limb.clone()), 235 | Some(res.clone()), 236 | None, 237 | None, 238 | ], 239 | [ 240 | None, 241 | None, 242 | None, 243 | None, 244 | None, 245 | None, 246 | Some(F::ONE), 247 | Some(-F::ONE), 248 | None, 249 | ], 250 | 9, 251 | )? 252 | .try_into() 253 | .unwrap(); 254 | 255 | // (res - 1) (abs - false_limb) 256 | self.config.assign_line( 257 | region, 258 | range_check_chip, 259 | offset, 260 | [ 261 | Some(res.clone()), 262 | Some(res.clone()), 263 | Some(abs.clone()), 264 | Some(false_limb), 265 | None, 266 | None, 267 | ], 268 | [ 269 | None, 270 | None, 271 | Some(-F::ONE), 272 | Some(F::ONE), 273 | None, 274 | None, 275 | Some(-F::ONE), 276 | Some(F::ONE), 277 | None, 278 | ], 279 | 0, 280 | )?; 281 | Ok(res.clone()) 282 | } 283 | 284 | fn lt_number( 285 | &self, 286 | region: &mut Region, 287 | range_check_chip: &mut RangeCheckChip, 288 | offset: &mut usize, 289 | lhs: &Number, 290 | rhs: &Number, 291 | ) -> Result<(), Error> { 292 | // gt2 means lhs[2] >= rhs[2] 293 | let gt2 = self.le_limb( 294 | region, 295 | range_check_chip, 296 | offset, 297 | &rhs.limbs[2], 298 | &lhs.limbs[2], 299 | )?; 300 | // gt1 means lhs[1] >= rhs[1] 301 | let gt1 = self.le_limb( 302 | region, 303 | range_check_chip, 304 | offset, 305 | &rhs.limbs[1], 306 | &lhs.limbs[1], 307 | )?; 308 | // gt0 means lhs[0] >= rhs[0] 309 | let gt0 = self.le_limb( 310 | region, 311 | range_check_chip, 312 | offset, 313 | &rhs.limbs[0], 314 | &lhs.limbs[0], 315 | )?; 316 | 317 | let zero = self 318 | .config 319 | .assign_constant(region, range_check_chip, offset, &F::ZERO)?; 320 | let one = self 321 | .config 322 | .assign_constant(region, range_check_chip, offset, &F::ONE)?; 323 | 324 | // if gt0 then zero else one means if lhs[0] < rhs[0] then one else zero 325 | let lt_0 = self 326 | .config 327 | .select(region, range_check_chip, offset, >0, &one, &zero, 0)?; 328 | 329 | // if gt1 then zero else one means if lhs[1] < rhs[1] then one else lt_0 330 | let lt_1 = self 331 | .config 332 | .select(region, range_check_chip, offset, >1, &one, <_0, 0)?; 333 | 334 | // if gt2 then zero else one means if lhs[2] < rhs[2] then one else lt_1 335 | let lt = self 336 | .config 337 | .select(region, range_check_chip, offset, >2, &one, <_1, 0)?; 338 | self.config 339 | .eq_constant(region, range_check_chip, offset, <, &F::ONE)?; 340 | Ok(()) 341 | } 342 | 343 | pub fn mod_add( 344 | &self, 345 | region: &mut Region, 346 | range_check_chip: &mut RangeCheckChip, 347 | offset: &mut usize, 348 | lhs: &Number, 349 | rhs: &Number, 350 | ) -> Result, Error> { 351 | let ret = lhs.add(rhs); 352 | let limbs = ret 353 | .limbs 354 | .into_iter() 355 | .enumerate() 356 | .map(|(i, l)| { 357 | let l = self 358 | .config 359 | .assign_line( 360 | region, 361 | range_check_chip, 362 | offset, 363 | [ 364 | Some(lhs.limbs[i].clone()), 365 | Some(rhs.limbs[i].clone()), 366 | None, 367 | None, 368 | Some(l), 369 | None, 370 | ], 371 | [ 372 | Some(F::ONE), 373 | Some(F::ONE), 374 | None, 375 | None, 376 | Some(F::ONE), 377 | None, 378 | None, 379 | None, 380 | None, 381 | ], 382 | 0, 383 | ) 384 | .unwrap(); 385 | l[2].clone() // the fifth is the sum result d 386 | }) 387 | .collect::>(); 388 | Ok(Number { 389 | limbs: limbs.try_into().unwrap(), 390 | }) 391 | } 392 | 393 | #[allow(clippy::too_many_arguments)] 394 | pub fn mod_native_mul( 395 | &self, 396 | region: &mut Region, 397 | range_check_chip: &mut RangeCheckChip, 398 | offset: &mut usize, 399 | rem: &Number, 400 | lhs: &Number, 401 | rhs: &Number, 402 | modulus: &Number, 403 | quotient: &Number, 404 | ) -> Result, Error> { 405 | let rem = self.config.assign_line( 406 | region, 407 | range_check_chip, 408 | offset, 409 | [ 410 | Some(modulus.limbs[3].clone()), 411 | Some(lhs.limbs[3].clone()), 412 | Some(rhs.limbs[3].clone()), 413 | Some(quotient.limbs[3].clone()), 414 | Some(rem.limbs[3].clone()), 415 | None, 416 | ], 417 | [ 418 | None, 419 | None, 420 | None, 421 | None, 422 | Some(-F::ONE), 423 | None, 424 | Some(-F::ONE), 425 | Some(F::ONE), 426 | None, 427 | ], 428 | 0, 429 | )?[4] 430 | .clone(); 431 | Ok(rem) 432 | } 433 | 434 | pub fn mod_power108m1( 435 | &self, 436 | region: &mut Region, 437 | range_check_chip: &mut RangeCheckChip, 438 | offset: &mut usize, 439 | number: &Number, 440 | ) -> Result<[Limb; 4], Error> { 441 | let value = number.limbs[0].value + number.limbs[1].value + number.limbs[2].value; 442 | let l = self.config.assign_line( 443 | region, 444 | range_check_chip, 445 | offset, 446 | [ 447 | Some(number.limbs[0].clone()), 448 | Some(number.limbs[1].clone()), 449 | Some(number.limbs[2].clone()), 450 | None, 451 | Some(Limb::new(None, value)), 452 | None, 453 | ], 454 | [ 455 | Some(F::ONE), 456 | Some(F::ONE), 457 | Some(F::ONE), 458 | None, 459 | Some(-F::ONE), 460 | None, 461 | None, 462 | None, 463 | None, 464 | ], 465 | 0, 466 | )?; 467 | Ok(l.try_into().unwrap()) 468 | } 469 | 470 | pub fn mod_power216( 471 | &self, 472 | region: &mut Region, 473 | range_check_chip: &mut RangeCheckChip, 474 | offset: &mut usize, 475 | number: &Number, 476 | ) -> Result, Error> { 477 | let value = number.limbs[0].value + number.limbs[1].value * (F::from_u128(1u128 << 108)); 478 | let l = self.config.assign_line( 479 | region, 480 | range_check_chip, 481 | offset, 482 | [ 483 | Some(number.limbs[0].clone()), 484 | Some(number.limbs[1].clone()), 485 | None, 486 | None, 487 | Some(Limb::new(None, value)), 488 | None, 489 | ], 490 | [ 491 | Some(F::ONE), 492 | Some(F::from_u128(1u128 << 108)), 493 | None, 494 | None, 495 | Some(-F::ONE), 496 | None, 497 | None, 498 | None, 499 | None, 500 | ], 501 | 0, 502 | )?; 503 | Ok(l[2].clone()) 504 | } 505 | 506 | pub fn mod_power108m1_mul( 507 | &self, 508 | region: &mut Region, 509 | range_check_chip: &mut RangeCheckChip, 510 | offset: &mut usize, 511 | lhs: &Number, 512 | rhs: &Number, 513 | ) -> Result, Error> { 514 | let bn_modulus = BigUint::from((1u128 << 108) - 1); 515 | let [_, _, _, ml] = self.mod_power108m1(region, range_check_chip, offset, lhs)?; // ml has at most 110 bits 516 | let [_, _, _, mr] = self.mod_power108m1(region, range_check_chip, offset, rhs)?; // mr has at most 110 bits 517 | let v = ml.value * mr.value; // at most 220 bits 518 | let bn_q = field_to_bn(&v).div(bn_modulus.clone()); // at most 112 bits 519 | let bn_r = field_to_bn(&v) - bn_q.clone() * bn_modulus; // at most 108 bits 520 | let q = Limb::new(None, bn_to_field(&bn_q)); 521 | let r = Limb::new(None, bn_to_field(&bn_r)); 522 | let l = self.config.assign_line( 523 | region, 524 | range_check_chip, 525 | offset, 526 | [Some(q), Some(ml), Some(mr), Some(r), None, None], 527 | [ 528 | Some(F::from_u128((1u128 << 108) - 1)), 529 | None, 530 | None, 531 | Some(F::ONE), 532 | None, 533 | None, 534 | None, 535 | Some(-F::ONE), 536 | None, 537 | ], 538 | 10, 539 | )?; 540 | Ok(l[3].clone()) 541 | } 542 | 543 | /// 544 | /// # Apply constraint: 545 | /// (r * 1 ) + (x0 * y0 * 1 ) + (v * 1 ) = 0 \ 546 | /// (ws[0] * cs[0]) + (ws[1] * ws[2] * cs[7]) + (ws[4] * cs[4]) = 0 547 | /// 548 | pub fn mod_power216_mul( 549 | &self, 550 | region: &mut Region, 551 | range_check_chip: &mut RangeCheckChip, 552 | offset: &mut usize, 553 | lhs: &Number, 554 | rhs: &Number, 555 | ) -> Result, Error> { 556 | let x0 = lhs.limbs[0].value; 557 | let x1 = lhs.limbs[1].value; 558 | let y0 = rhs.limbs[0].value; 559 | let y1 = rhs.limbs[1].value; 560 | let mut v = x0 * y1 + x1 * y0; //0-2^216 561 | let l = self.config.assign_line( 562 | region, 563 | range_check_chip, 564 | offset, 565 | [ 566 | Some(lhs.limbs[0].clone()), //x0 567 | Some(lhs.limbs[1].clone()), //x1 568 | Some(rhs.limbs[0].clone()), //y0 569 | Some(rhs.limbs[1].clone()), //y1 570 | Some(Limb::new(None, v)), 571 | None, 572 | ], 573 | [ 574 | None, 575 | None, 576 | None, 577 | None, 578 | Some(-F::ONE), 579 | None, 580 | Some(F::ONE), 581 | Some(F::ONE), 582 | None, 583 | ], 584 | 0, 585 | )?; 586 | let vcell = l[4].clone(); 587 | 588 | // compute v mod 2^108 589 | let bn_q = field_to_bn(&v).div(BigUint::from(1u128 << 108)); 590 | let bn_r = field_to_bn(&v) - bn_q.clone() * BigUint::from(1u128 << 108); 591 | let q = Limb::new(None, bn_to_field(&bn_q)); 592 | let r = Limb::new(None, bn_to_field(&bn_r)); 593 | 594 | let l = self.config.assign_line( 595 | region, 596 | range_check_chip, 597 | offset, 598 | [Some(q), Some(vcell), None, Some(r), None, None], 599 | [ 600 | Some(F::from_u128(1u128 << 108)), 601 | Some(-F::ONE), 602 | None, 603 | Some(F::ONE), 604 | None, 605 | None, 606 | None, 607 | None, 608 | None, 609 | ], 610 | 10, 611 | )?; 612 | let rcell = l[2].clone(); 613 | v = rcell.value * F::from_u128(1u128 << 108) + x0 * y0; 614 | 615 | let l = self.config.assign_line( 616 | region, 617 | range_check_chip, 618 | offset, 619 | [ 620 | Some(rcell), 621 | Some(lhs.limbs[0].clone()), 622 | Some(rhs.limbs[0].clone()), 623 | None, 624 | Some(Limb::new(None, v)), 625 | None, 626 | ], 627 | [ 628 | Some(F::from_u128(1u128 << 108)), 629 | None, 630 | None, 631 | None, 632 | Some(-F::ONE), 633 | None, 634 | None, 635 | Some(F::ONE), 636 | None, 637 | ], 638 | 0, // TODO: need to check rcell range 639 | )?; 640 | Ok(l[3].clone()) 641 | } 642 | 643 | pub fn mod_power108m1_zero( 644 | &self, 645 | region: &mut Region, 646 | range_check_chip: &mut RangeCheckChip, 647 | offset: &mut usize, 648 | limbs: Vec>, 649 | signs: Vec, 650 | ) -> Result<(), Error> { 651 | let c = (1u128 << 108) - 1; 652 | let v = F::from_u128(c * 16u128) 653 | + limbs[0].value * signs[0] 654 | + limbs[1].value * signs[1] 655 | + limbs[2].value * signs[2]; 656 | let q = field_to_bn(&v).div(c); 657 | self.config.assign_line( 658 | region, 659 | range_check_chip, 660 | offset, 661 | [ 662 | Some(Limb::new(None, bn_to_field(&q))), 663 | Some(limbs[0].clone()), 664 | Some(limbs[1].clone()), 665 | Some(limbs[2].clone()), 666 | None, 667 | None, 668 | ], 669 | [ 670 | Some(-F::from_u128(c)), 671 | Some(signs[0]), 672 | Some(signs[1]), 673 | Some(signs[2]), 674 | None, 675 | None, 676 | None, 677 | None, 678 | Some(F::from_u128(c * 16u128)), 679 | ], 680 | 1, // check rcell range 681 | )?; 682 | Ok(()) 683 | } 684 | 685 | /// 686 | /// # Apply constraint: 687 | /// (q * -c) + (sum(limb_i * sign_i)) + (c * BUFMULT) = 0 688 | /// 689 | pub fn mod_power216_zero( 690 | &self, 691 | region: &mut Region, 692 | range_check_chip: &mut RangeCheckChip, 693 | offset: &mut usize, 694 | limbs: Vec>, 695 | signs: Vec, 696 | ) -> Result<(), Error> { 697 | // BUFMULT = 2 may cause overflow 698 | const BUFMULT: u128 = 8u128; // as long as its > 2-bits wide. 699 | let f_c = F::from_u128(1u128 << 108) * F::from_u128(1u128 << 108); 700 | let f_cm = f_c * F::from_u128(BUFMULT); 701 | let v = (f_c * F::from_u128(BUFMULT)) 702 | + limbs[0].value * signs[0] 703 | + limbs[1].value * signs[1] 704 | + limbs[2].value * signs[2]; 705 | let q = field_to_bn(&v).div(field_to_bn(&f_c)); 706 | self.config.assign_line( 707 | region, 708 | range_check_chip, 709 | offset, 710 | [ 711 | Some(Limb::new(None, bn_to_field(&q))), 712 | Some(limbs[0].clone()), 713 | Some(limbs[1].clone()), 714 | Some(limbs[2].clone()), 715 | None, 716 | None, 717 | ], 718 | [ 719 | Some(-f_c), 720 | Some(signs[0]), 721 | Some(signs[1]), 722 | Some(signs[2]), 723 | None, 724 | None, 725 | None, 726 | None, 727 | Some(f_cm), 728 | ], 729 | 1, // check rcell range 730 | )?; 731 | Ok(()) 732 | } 733 | 734 | // return 0 if not zero, 1 if zero for number 735 | pub fn number_is_zero( 736 | &self, 737 | region: &mut Region, 738 | range_check_chip: &mut RangeCheckChip, 739 | offset: &mut usize, 740 | number: &Number, 741 | ) -> Result, Error> { 742 | let zero = F::ZERO; 743 | let three = F::from(3u64); 744 | // limb0_zero is 0 if not zero, 1 if zero 745 | let limb0_zero = 746 | self.config 747 | .eq_constant(region, range_check_chip, offset, &number.limbs[0], &zero)?; 748 | let limb1_zero = 749 | self.config 750 | .eq_constant(region, range_check_chip, offset, &number.limbs[1], &zero)?; 751 | let limb2_zero = 752 | self.config 753 | .eq_constant(region, range_check_chip, offset, &number.limbs[2], &zero)?; 754 | 755 | // all the above zero flat is either zero or one thus bounded 756 | // thus check all of them are 1 equals to check the sum of them are 3 757 | let sum: Limb = self.config.assign_line( 758 | region, 759 | range_check_chip, 760 | offset, 761 | [ 762 | Some(limb0_zero.clone()), 763 | Some(limb1_zero.clone()), 764 | Some(limb2_zero.clone()), 765 | None, 766 | Some(Limb::new( 767 | None, 768 | limb0_zero.value + limb1_zero.value + limb2_zero.value, 769 | )), 770 | None, 771 | ], 772 | [ 773 | Some(F::ONE), 774 | Some(F::ONE), 775 | Some(F::ONE), 776 | None, 777 | Some(-F::ONE), 778 | None, 779 | None, 780 | None, 781 | None, 782 | ], 783 | 0, 784 | )?[3] 785 | .clone(); 786 | let is_zero = self 787 | .config 788 | .eq_constant(region, range_check_chip, offset, &sum, &three)?; 789 | Ok(is_zero) 790 | } 791 | 792 | pub fn mod_mult( 793 | &self, 794 | region: &mut Region, 795 | range_check_chip: &mut RangeCheckChip, 796 | offset: &mut usize, 797 | lhs: &Number, 798 | rhs: &Number, 799 | modulus: &Number, 800 | ) -> Result, Error> { 801 | let one = self.assign_number( 802 | region, 803 | range_check_chip, 804 | offset, 805 | Number::from_bn(&BigUint::from(1u128)), 806 | )?; 807 | let zero = self.assign_number( 808 | region, 809 | range_check_chip, 810 | offset, 811 | Number::from_bn(&BigUint::from(0u128)), 812 | )?; 813 | let is_zero = self.number_is_zero(region, range_check_chip, offset, modulus)?; 814 | let modulus_mock: Number = 815 | self.select(region, range_check_chip, offset, &is_zero, modulus, &one)?; 816 | let r: Number = 817 | self.mod_mult_unsafe(region, range_check_chip, offset, lhs, rhs, &modulus_mock)?; 818 | let res = self.select(region, range_check_chip, offset, &is_zero, &r, &zero)?; 819 | self.lt_number(region, range_check_chip, offset, &res, modulus)?; 820 | Ok(res) 821 | } 822 | 823 | pub fn mod_mult_unsafe( 824 | &self, 825 | region: &mut Region, 826 | range_check_chip: &mut RangeCheckChip, 827 | offset: &mut usize, 828 | lhs: &Number, 829 | rhs: &Number, 830 | modulus: &Number, 831 | ) -> Result, Error> { 832 | /* 833 | * x0,x1,x2 * y0,y1,y2 = q0,q1,q2 * m0,m1,m2 + r0,r1,r2 834 | * mod 2^{108}-1: 835 | * (x2+x1+x0)*(y0+y1+y2) = (q0+q1+q2)*(m0+m1+m2)+(r0+r1+r2) 836 | * mod 2^{216}: 837 | * (x1*y0+x0*y1)*2^216+x0*y0 = (q0*m1+q1*m0)*2^{216}+q0*m0+r1+r0 838 | * native: 839 | * x*y = q*m + r 840 | */ 841 | 842 | // TODO: mod p first to avoid small p, k overflow 843 | // lhs % p assign_line constraint 844 | // rhs % p assign_line constraint 845 | let bn_lhs = lhs.to_bn(); 846 | let bn_rhs = rhs.to_bn(); 847 | let bn_mult = bn_lhs.mul(bn_rhs); 848 | let bn_modulus = modulus.to_bn(); 849 | let bn_quotient = bn_mult.clone().div(bn_modulus.clone()); //div_rem 850 | let bn_rem = bn_mult - (bn_quotient.clone() * bn_modulus.clone()); 851 | let modulus = self.assign_number( 852 | region, 853 | range_check_chip, 854 | offset, 855 | Number::from_bn(&bn_modulus), 856 | )?; 857 | let rem = self.assign_number(region, range_check_chip, offset, Number::from_bn(&bn_rem))?; 858 | let quotient = self.assign_number( 859 | region, 860 | range_check_chip, 861 | offset, 862 | Number::from_bn(&bn_quotient), 863 | )?; 864 | let mod_108m1_lhs = self.mod_power108m1_mul(region, range_check_chip, offset, lhs, rhs)?; 865 | let mod_108m1_rhs = 866 | self.mod_power108m1_mul(region, range_check_chip, offset, "ient, &modulus)?; 867 | let [r0, r1, r2, mod_108m1_rem] = 868 | self.mod_power108m1(region, range_check_chip, offset, &rem)?; 869 | self.mod_power108m1_zero( 870 | region, 871 | range_check_chip, 872 | offset, 873 | vec![ 874 | mod_108m1_lhs.clone(), 875 | mod_108m1_rhs.clone(), 876 | mod_108m1_rem.clone(), 877 | ], 878 | vec![F::ONE, -F::ONE, -F::ONE], 879 | )?; 880 | let mod_216_lhs = self.mod_power216_mul(region, range_check_chip, offset, lhs, rhs)?; 881 | let mod_216_rhs = 882 | self.mod_power216_mul(region, range_check_chip, offset, "ient, &modulus)?; 883 | let mod_216_rem = self.mod_power216(region, range_check_chip, offset, &rem)?; 884 | 885 | self.mod_power216_zero( 886 | region, 887 | range_check_chip, 888 | offset, 889 | vec![mod_216_lhs, mod_216_rhs, mod_216_rem], 890 | vec![F::ONE, -F::ONE, -F::ONE], 891 | )?; 892 | let mod_native = self.mod_native_mul( 893 | region, 894 | range_check_chip, 895 | offset, 896 | &rem, 897 | lhs, 898 | rhs, 899 | &modulus, 900 | "ient, 901 | )?; 902 | Ok(Number { 903 | limbs: [r0, r1, r2, mod_native], 904 | }) 905 | } 906 | 907 | /// Selects result based on the condition cond = '1' or '0' \ 908 | /// result = cond * base + (1-cond) * one 909 | /// 910 | pub fn select( 911 | &self, 912 | region: &mut Region, 913 | range_check_chip: &mut RangeCheckChip, 914 | offset: &mut usize, 915 | cond: &Limb, 916 | one: &Number, 917 | base: &Number, 918 | ) -> Result, Error> { 919 | let mut limbs = vec![]; 920 | for i in 0..4 { 921 | let l = self.config.select( 922 | region, 923 | range_check_chip, 924 | offset, 925 | cond, 926 | &one.limbs[i], 927 | &base.limbs[i], 928 | 0, 929 | )?; 930 | limbs.push(l); 931 | } 932 | Ok(Number { 933 | limbs: limbs.try_into().unwrap(), 934 | }) 935 | } 936 | 937 | pub fn mod_exp( 938 | &self, 939 | region: &mut Region, 940 | range_check_chip: &mut RangeCheckChip, 941 | offset: &mut usize, 942 | base: &Number, 943 | exp: &Number, 944 | modulus: &Number, 945 | ) -> Result, Error> { 946 | let mut limbs = vec![]; 947 | self.config.decompose_limb( 948 | region, 949 | range_check_chip, 950 | offset, 951 | &exp.limbs[2], 952 | &mut limbs, 953 | 40, 954 | )?; //256 - 216 = 40 955 | self.config.decompose_limb( 956 | region, 957 | range_check_chip, 958 | offset, 959 | &exp.limbs[1], 960 | &mut limbs, 961 | 108, 962 | )?; 963 | self.config.decompose_limb( 964 | region, 965 | range_check_chip, 966 | offset, 967 | &exp.limbs[0], 968 | &mut limbs, 969 | 108, 970 | )?; 971 | let mut acc = self.assign_constant( 972 | region, 973 | range_check_chip, 974 | offset, 975 | Number::from_bn(&BigUint::from(1_u128)), 976 | 0, 977 | )?; 978 | let one = acc.clone(); 979 | for limb in limbs.iter() { 980 | acc = self.mod_mult(region, range_check_chip, offset, &acc, &acc, modulus)?; 981 | let sval = self.select(region, range_check_chip, offset, limb, &one, base)?; 982 | acc = self.mod_mult(region, range_check_chip, offset, &acc, &sval, modulus)?; 983 | } 984 | Ok(acc) 985 | } 986 | } 987 | 988 | #[cfg(test)] 989 | mod tests { 990 | use crate::circuits::range::{RangeCheckChip, RangeCheckConfig}; 991 | use crate::circuits::CommonGateConfig; 992 | use crate::value_for_assign; 993 | use halo2_gate_generator::Limb; 994 | use halo2_proofs::dev::MockProver; 995 | use halo2_proofs::halo2curves::bn256::Fr; 996 | use num_bigint::BigUint; 997 | 998 | use halo2_proofs::{ 999 | circuit::{Chip, Layouter, Region, SimpleFloorPlanner}, 1000 | plonk::{Advice, Circuit, Column, ConstraintSystem, Error}, 1001 | }; 1002 | 1003 | use super::{ModExpChip, Number}; 1004 | 1005 | #[derive(Clone, Debug)] 1006 | pub struct HelperChipConfig { 1007 | limb: Column, 1008 | } 1009 | 1010 | #[derive(Clone, Debug)] 1011 | pub struct HelperChip { 1012 | config: HelperChipConfig, 1013 | } 1014 | 1015 | impl Chip for HelperChip { 1016 | type Config = HelperChipConfig; 1017 | type Loaded = (); 1018 | 1019 | fn config(&self) -> &Self::Config { 1020 | &self.config 1021 | } 1022 | 1023 | fn loaded(&self) -> &Self::Loaded { 1024 | &() 1025 | } 1026 | } 1027 | 1028 | impl HelperChip { 1029 | fn new(config: HelperChipConfig) -> Self { 1030 | HelperChip { config } 1031 | } 1032 | 1033 | fn configure(cs: &mut ConstraintSystem) -> HelperChipConfig { 1034 | let limb = cs.advice_column(); 1035 | cs.enable_equality(limb); 1036 | HelperChipConfig { limb } 1037 | } 1038 | 1039 | fn assign_base( 1040 | &self, 1041 | _region: &mut Region, 1042 | _offset: &mut usize, 1043 | base: &BigUint, 1044 | ) -> Result, Error> { 1045 | Ok(Number::from_bn(base)) 1046 | } 1047 | 1048 | fn assign_modulus( 1049 | &self, 1050 | _region: &mut Region, 1051 | _offset: &mut usize, 1052 | modulus: &BigUint, 1053 | ) -> Result, Error> { 1054 | Ok(Number::from_bn(modulus)) 1055 | } 1056 | 1057 | fn assign_exp( 1058 | &self, 1059 | _region: &mut Region, 1060 | _offset: &mut usize, 1061 | exponent: &BigUint, 1062 | ) -> Result, Error> { 1063 | Ok(Number::from_bn(exponent)) 1064 | } 1065 | 1066 | fn assign_results( 1067 | &self, 1068 | region: &mut Region, 1069 | offset: &mut usize, 1070 | result: &BigUint, 1071 | ) -> Result, Error> { 1072 | let n = Number::from_bn(result); 1073 | let mut cells = vec![]; 1074 | for i in 0..4 { 1075 | let c = region.assign_advice( 1076 | || format!("assign input"), 1077 | self.config.limb, 1078 | *offset + i, 1079 | || value_for_assign!(n.limbs[i].value), 1080 | )?; 1081 | cells.push(Some(c)); 1082 | *offset = *offset + 1; 1083 | } 1084 | let n = Number { 1085 | limbs: [ 1086 | Limb::new(cells[0].clone(), n.limbs[0].value), 1087 | Limb::new(cells[1].clone(), n.limbs[1].value), 1088 | Limb::new(cells[2].clone(), n.limbs[2].value), 1089 | Limb::new(cells[3].clone(), n.limbs[3].value), 1090 | ], 1091 | }; 1092 | Ok(n) 1093 | } 1094 | } 1095 | 1096 | use num_bigint::RandomBits; 1097 | use rand::{thread_rng, Rng}; 1098 | const LIMB_WIDTH: usize = 108; 1099 | 1100 | use std::ops::{Div, Mul}; 1101 | 1102 | #[derive(Clone, Debug)] 1103 | struct TestConfig { 1104 | modexpconfig: CommonGateConfig, 1105 | helperconfig: HelperChipConfig, 1106 | rangecheckconfig: RangeCheckConfig, 1107 | } 1108 | 1109 | #[derive(Clone, Debug, Default)] 1110 | struct TestModpower108m1Circuit { 1111 | a: BigUint, 1112 | bn_test_res: BigUint, 1113 | } 1114 | 1115 | impl Circuit for TestModpower108m1Circuit { 1116 | type Config = TestConfig; 1117 | type FloorPlanner = SimpleFloorPlanner; 1118 | 1119 | fn without_witnesses(&self) -> Self { 1120 | Self::default() 1121 | } 1122 | 1123 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1124 | let rangecheckconfig = RangeCheckChip::::configure(meta); 1125 | Self::Config { 1126 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1127 | helperconfig: HelperChip::configure(meta), 1128 | rangecheckconfig, 1129 | } 1130 | } 1131 | 1132 | fn synthesize( 1133 | &self, 1134 | config: Self::Config, 1135 | mut layouter: impl Layouter, 1136 | ) -> Result<(), Error> { 1137 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1138 | let helperchip = HelperChip::new(config.clone().helperconfig); 1139 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1140 | layouter.assign_region( 1141 | || "mod_power108m1", 1142 | |mut region| { 1143 | range_chip.initialize(&mut region)?; 1144 | let mut offset = 0; 1145 | let a = helperchip.assign_base(&mut region, &mut offset, &self.a)?; 1146 | let result = 1147 | helperchip.assign_results(&mut region, &mut offset, &self.bn_test_res)?; 1148 | let rem = 1149 | modexpchip.mod_power108m1(&mut region, &mut range_chip, &mut offset, &a)?; 1150 | println!("\nbn_res \t\t= 0x{}", &self.bn_test_res.to_str_radix(16)); 1151 | println!("\nrem is (hex):"); 1152 | for i in 0..4 { 1153 | println!("rem[{i}] \t\t= {:?}", &rem[i].value); 1154 | } 1155 | for i in 0..4 { 1156 | println!("result[{}] \t= {:?}", i, &result.limbs[i].value); 1157 | /* not equal since might overflow at most of 2 bits 1158 | region.constrain_equal( 1159 | rem[i].clone().cell.unwrap().cell(), 1160 | result.limbs[i].clone().cell.unwrap().cell() 1161 | )?; 1162 | */ 1163 | } 1164 | Ok(rem) 1165 | }, 1166 | )?; 1167 | Ok(()) 1168 | } 1169 | } 1170 | 1171 | #[derive(Clone, Debug, Default)] 1172 | struct TestModpower108m1mulCircuit { 1173 | a: BigUint, 1174 | b: BigUint, 1175 | bn_test_res: BigUint, 1176 | } 1177 | 1178 | impl Circuit for TestModpower108m1mulCircuit { 1179 | type Config = TestConfig; 1180 | type FloorPlanner = SimpleFloorPlanner; 1181 | 1182 | fn without_witnesses(&self) -> Self { 1183 | Self::default() 1184 | } 1185 | 1186 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1187 | let rangecheckconfig = RangeCheckChip::::configure(meta); 1188 | Self::Config { 1189 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1190 | helperconfig: HelperChip::configure(meta), 1191 | rangecheckconfig, 1192 | } 1193 | } 1194 | 1195 | fn synthesize( 1196 | &self, 1197 | config: Self::Config, 1198 | mut layouter: impl Layouter, 1199 | ) -> Result<(), Error> { 1200 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1201 | let helperchip = HelperChip::new(config.clone().helperconfig); 1202 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1203 | layouter.assign_region( 1204 | || "mod_power108m1_mul", 1205 | |mut region| { 1206 | range_chip.initialize(&mut region)?; 1207 | let mut offset = 0; 1208 | let _result = 1209 | helperchip.assign_results(&mut region, &mut offset, &self.bn_test_res)?; 1210 | let lhs = helperchip.assign_modulus(&mut region, &mut offset, &self.a)?; 1211 | let rhs = helperchip.assign_base(&mut region, &mut offset, &self.b)?; 1212 | let res = modexpchip.mod_power108m1_mul( 1213 | &mut region, 1214 | &mut range_chip, 1215 | &mut offset, 1216 | &lhs, 1217 | &rhs, 1218 | )?; 1219 | println!("\nbn_rem \t= {:?}", &self.bn_test_res.to_str_radix(16)); 1220 | println!("result is \t= {:?}", res.value); 1221 | Ok(res) 1222 | }, 1223 | )?; 1224 | Ok(()) 1225 | } 1226 | } 1227 | 1228 | #[derive(Clone, Debug, Default)] 1229 | struct TestModPower216MulCircuit { 1230 | l: BigUint, 1231 | r: BigUint, 1232 | bn_test_res: BigUint, 1233 | } 1234 | 1235 | impl Circuit for TestModPower216MulCircuit { 1236 | type Config = TestConfig; 1237 | type FloorPlanner = SimpleFloorPlanner; 1238 | 1239 | fn without_witnesses(&self) -> Self { 1240 | Self::default() 1241 | } 1242 | 1243 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1244 | let rangecheckconfig = RangeCheckChip::::configure(meta); 1245 | Self::Config { 1246 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1247 | helperconfig: HelperChip::configure(meta), 1248 | rangecheckconfig, 1249 | } 1250 | } 1251 | 1252 | fn synthesize( 1253 | &self, 1254 | config: Self::Config, 1255 | mut layouter: impl Layouter, 1256 | ) -> Result<(), Error> { 1257 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1258 | let helperchip = HelperChip::new(config.clone().helperconfig); 1259 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1260 | layouter.assign_region( 1261 | || "test circuit mod_power216_mul", 1262 | |mut region| { 1263 | range_chip.initialize(&mut region)?; 1264 | let mut offset = 0; 1265 | let _result = 1266 | helperchip.assign_results(&mut region, &mut offset, &self.bn_test_res)?; 1267 | let lhs = helperchip.assign_base(&mut region, &mut offset, &self.l)?; 1268 | let rhs = helperchip.assign_base(&mut region, &mut offset, &self.r)?; 1269 | let res = modexpchip.mod_power216_mul( 1270 | &mut region, 1271 | &mut range_chip, 1272 | &mut offset, 1273 | &lhs, 1274 | &rhs, 1275 | )?; 1276 | println!("\nbn_rem \t= {}", &self.bn_test_res.to_str_radix(16)); 1277 | println!("res \t= {:?}", res.value); 1278 | Ok(res) 1279 | }, 1280 | )?; 1281 | Ok(()) 1282 | } 1283 | } 1284 | 1285 | #[derive(Clone, Debug, Default)] 1286 | struct Test108m1v216Circuit { 1287 | l: BigUint, 1288 | r: BigUint, 1289 | modulus: BigUint, 1290 | quotient: BigUint, 1291 | rem: BigUint, 1292 | } 1293 | 1294 | impl Circuit for Test108m1v216Circuit { 1295 | type Config = TestConfig; 1296 | type FloorPlanner = SimpleFloorPlanner; 1297 | 1298 | fn without_witnesses(&self) -> Self { 1299 | Self::default() 1300 | } 1301 | 1302 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1303 | let rangecheckconfig: RangeCheckConfig = RangeCheckChip::::configure(meta); 1304 | Self::Config { 1305 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1306 | helperconfig: HelperChip::configure(meta), 1307 | rangecheckconfig, 1308 | } 1309 | } 1310 | 1311 | fn synthesize( 1312 | &self, 1313 | config: Self::Config, 1314 | mut layouter: impl Layouter, 1315 | ) -> Result<(), Error> { 1316 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1317 | let helperchip = HelperChip::new(config.clone().helperconfig); 1318 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1319 | layouter.assign_region( 1320 | || "test circuit 108m1 vs 216", 1321 | |mut region| { 1322 | range_chip.initialize(&mut region)?; 1323 | let mut offset = 0; 1324 | let lhs = helperchip.assign_base(&mut region, &mut offset, &self.l)?; 1325 | let rhs = helperchip.assign_base(&mut region, &mut offset, &self.r)?; 1326 | let modulus = 1327 | helperchip.assign_modulus(&mut region, &mut offset, &self.modulus)?; 1328 | let quo = helperchip.assign_base(&mut region, &mut offset, &self.quotient)?; 1329 | let rem = helperchip.assign_base(&mut region, &mut offset, &self.rem)?; 1330 | let rl_mod_108m1 = modexpchip.mod_power108m1_mul( 1331 | &mut region, 1332 | &mut range_chip, 1333 | &mut offset, 1334 | &lhs, 1335 | &rhs, 1336 | )?; 1337 | let qm_mod_108m1 = modexpchip.mod_power108m1_mul( 1338 | &mut region, 1339 | &mut range_chip, 1340 | &mut offset, 1341 | &quo, 1342 | &modulus, 1343 | )?; 1344 | let [_r0, _r1, _r2, rem_mod_108m1] = modexpchip.mod_power108m1( 1345 | &mut region, 1346 | &mut range_chip, 1347 | &mut offset, 1348 | &rem, 1349 | )?; 1350 | let lr_mod_216 = modexpchip.mod_power216_mul( 1351 | &mut region, 1352 | &mut range_chip, 1353 | &mut offset, 1354 | &lhs, 1355 | &rhs, 1356 | )?; 1357 | let qm_mod_216 = modexpchip.mod_power216_mul( 1358 | &mut region, 1359 | &mut range_chip, 1360 | &mut offset, 1361 | &quo, 1362 | &modulus, 1363 | )?; 1364 | let rem_mod_216: Limb = 1365 | modexpchip.mod_power216(&mut region, &mut range_chip, &mut offset, &rem)?; 1366 | println!( 1367 | "rl_mod_108m1 {:?} = {:?} lr_mod_216", 1368 | rl_mod_108m1.value, lr_mod_216.value 1369 | ); 1370 | println!( 1371 | "qm_mod_108m1 {:?} = {:?} qm_mod_216", 1372 | qm_mod_108m1.value, qm_mod_216.value 1373 | ); 1374 | println!( 1375 | "rem_mod_108m1 {:?} = {:?} mod_216_rem", 1376 | rem_mod_108m1.value, rem_mod_216.value 1377 | ); 1378 | println!(""); 1379 | region.constrain_equal( 1380 | rl_mod_108m1.clone().cell.unwrap().cell(), 1381 | lr_mod_216.clone().cell.unwrap().cell(), 1382 | )?; 1383 | region.constrain_equal( 1384 | qm_mod_108m1.clone().cell.unwrap().cell(), 1385 | qm_mod_216.clone().cell.unwrap().cell(), 1386 | )?; 1387 | region.constrain_equal( 1388 | rem_mod_108m1.clone().cell.unwrap().cell(), 1389 | rem_mod_216.clone().cell.unwrap().cell(), 1390 | )?; 1391 | Ok(rem) 1392 | }, 1393 | )?; 1394 | Ok(()) 1395 | } 1396 | } 1397 | 1398 | #[derive(Clone, Debug, Default)] 1399 | struct TestModMultCircuit { 1400 | l: BigUint, 1401 | r: BigUint, 1402 | modulus: BigUint, 1403 | bn_test_res: BigUint, 1404 | } 1405 | 1406 | impl Circuit for TestModMultCircuit { 1407 | type Config = TestConfig; 1408 | type FloorPlanner = SimpleFloorPlanner; 1409 | 1410 | fn without_witnesses(&self) -> Self { 1411 | Self::default() 1412 | } 1413 | 1414 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1415 | let rangecheckconfig = RangeCheckChip::::configure(meta); 1416 | Self::Config { 1417 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1418 | helperconfig: HelperChip::configure(meta), 1419 | rangecheckconfig, 1420 | } 1421 | } 1422 | 1423 | fn synthesize( 1424 | &self, 1425 | config: Self::Config, 1426 | mut layouter: impl Layouter, 1427 | ) -> Result<(), Error> { 1428 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1429 | let helperchip = HelperChip::new(config.clone().helperconfig); 1430 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1431 | layouter.assign_region( 1432 | || "test circuit mod_mult", 1433 | |mut region| { 1434 | range_chip.initialize(&mut region)?; 1435 | let mut offset = 0; 1436 | let result = 1437 | helperchip.assign_results(&mut region, &mut offset, &self.bn_test_res)?; 1438 | let modulus = 1439 | helperchip.assign_modulus(&mut region, &mut offset, &self.modulus)?; 1440 | let lhs = helperchip.assign_base(&mut region, &mut offset, &self.l)?; 1441 | let rhs = helperchip.assign_base(&mut region, &mut offset, &self.r)?; 1442 | let rem = modexpchip.mod_mult( 1443 | &mut region, 1444 | &mut range_chip, 1445 | &mut offset, 1446 | &lhs, 1447 | &rhs, 1448 | &modulus, 1449 | )?; 1450 | for i in 0..4 { 1451 | println!( 1452 | "rem is {:?}, result is {:?}", 1453 | &rem.limbs[i].value, &result.limbs[i].value 1454 | ); 1455 | println!( 1456 | "remcell is {:?}, resultcell is {:?}", 1457 | &rem.limbs[i].cell.as_ref().unwrap().value(), 1458 | &result.limbs[i].cell.as_ref().unwrap().value() 1459 | ); 1460 | region.constrain_equal( 1461 | rem.limbs[i].clone().cell.unwrap().cell(), 1462 | result.limbs[i].clone().cell.unwrap().cell(), 1463 | )?; 1464 | } 1465 | Ok(rem) 1466 | }, 1467 | )?; 1468 | Ok(()) 1469 | } 1470 | } 1471 | 1472 | #[derive(Clone, Debug, Default)] 1473 | struct TestModExpCircuit { 1474 | base: BigUint, 1475 | exp: BigUint, 1476 | modulus: BigUint, 1477 | bn_test_res: BigUint, 1478 | } 1479 | 1480 | impl Circuit for TestModExpCircuit { 1481 | type Config = TestConfig; 1482 | type FloorPlanner = SimpleFloorPlanner; 1483 | 1484 | fn without_witnesses(&self) -> Self { 1485 | Self::default() 1486 | } 1487 | 1488 | fn configure(meta: &mut ConstraintSystem) -> Self::Config { 1489 | let rangecheckconfig = RangeCheckChip::::configure(meta); 1490 | Self::Config { 1491 | modexpconfig: ModExpChip::::configure(meta, &rangecheckconfig), 1492 | helperconfig: HelperChip::configure(meta), 1493 | rangecheckconfig, 1494 | } 1495 | } 1496 | 1497 | fn synthesize( 1498 | &self, 1499 | config: Self::Config, 1500 | mut layouter: impl Layouter, 1501 | ) -> Result<(), Error> { 1502 | let modexpchip = ModExpChip::::new(config.clone().modexpconfig); 1503 | let helperchip = HelperChip::new(config.clone().helperconfig); 1504 | let mut range_chip = RangeCheckChip::::new(config.clone().rangecheckconfig); 1505 | layouter.assign_region( 1506 | || "assign mod exp", 1507 | |mut region| { 1508 | range_chip.initialize(&mut region)?; 1509 | let mut offset = 0; 1510 | let base = helperchip.assign_base(&mut region, &mut offset, &self.base)?; 1511 | let exp = helperchip.assign_exp(&mut region, &mut offset, &self.exp)?; 1512 | let modulus = 1513 | helperchip.assign_modulus(&mut region, &mut offset, &self.modulus)?; 1514 | let result = 1515 | helperchip.assign_results(&mut region, &mut offset, &self.bn_test_res)?; 1516 | let rem = modexpchip.mod_exp( 1517 | &mut region, 1518 | &mut range_chip, 1519 | &mut offset, 1520 | &base, 1521 | &exp, 1522 | &modulus, 1523 | )?; 1524 | for i in 0..4 { 1525 | // println!("rem is {:?}, \t result is {:?}", &rem.limbs[i].value, &result.limbs[i].value); 1526 | // println!("remcell is \t{:?}", &rem.limbs[i].cell); 1527 | // println!("resultcell is \t {:?}", &result.limbs[i].cell); 1528 | region.constrain_equal( 1529 | rem.limbs[i].clone().cell.unwrap().cell(), 1530 | result.limbs[i].clone().cell.unwrap().cell(), 1531 | )?; 1532 | } 1533 | // println!("offset final {}", offset); 1534 | Ok(()) 1535 | }, 1536 | )?; 1537 | Ok(()) 1538 | } 1539 | } 1540 | 1541 | fn run_mod_power108m1_circuit() -> Result<(), CircuitError> { 1542 | // Create an a set of test vectors varying in bit lengths. 1543 | // Test will pass if: 1544 | // (1) result returned from circuit constrain_equal() to the 1545 | // assigned result from bn calculation. 1546 | // (2) prover.verify() for each run verifies without error. 1547 | let mut bn_test_vectors: Vec = Vec::with_capacity(8); 1548 | let bit_len: [usize; 8] = [ 1549 | 0, 1550 | 1, 1551 | LIMB_WIDTH + LIMB_WIDTH + LIMB_WIDTH + 1, 1552 | LIMB_WIDTH, 1553 | LIMB_WIDTH + 1, 1554 | LIMB_WIDTH + 108, 1555 | LIMB_WIDTH + LIMB_WIDTH + 1, 1556 | LIMB_WIDTH + LIMB_WIDTH + 108, 1557 | ]; 1558 | for i in 0..8 { 1559 | bn_test_vectors.push(get_random_x_bit_bn(bit_len[i])); 1560 | } 1561 | for testcase in bn_test_vectors { 1562 | let (a_l2, a_l1, a_l0) = get_limbs_from_bn(&testcase); 1563 | let bn_l210sum = &a_l2 + &a_l1 + &a_l0; 1564 | let bn_test_res = bn_l210sum; 1565 | let a = testcase.clone(); 1566 | let test_circuit = TestModpower108m1Circuit { a, bn_test_res }; 1567 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1568 | Ok(prover_run) => prover_run, 1569 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1570 | }; 1571 | match prover.verify() { 1572 | Ok(_) => (), 1573 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1574 | }; 1575 | } 1576 | Ok(()) 1577 | } 1578 | 1579 | fn run_mod_power108m1_mul_circuit() -> Result<(), CircuitError> { 1580 | let a = get_random_x_bit_bn(LIMB_WIDTH + 42); 1581 | let b = get_random_x_bit_bn(47); 1582 | let modulus = BigUint::parse_bytes(b"fffffffffffffffffffffffffff", 16).unwrap(); 1583 | 1584 | let bn_test_res = (a.clone() * b.clone()) % modulus; 1585 | let test_circuit = TestModpower108m1mulCircuit { a, b, bn_test_res }; 1586 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1587 | Ok(prover_run) => prover_run, 1588 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1589 | }; 1590 | match prover.verify() { 1591 | Ok(_) => (), 1592 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1593 | }; 1594 | Ok(()) 1595 | } 1596 | 1597 | fn run_mod_power216_mul_circuit() -> Result<(), CircuitError> { 1598 | let l = get_random_x_bit_bn(LIMB_WIDTH + LIMB_WIDTH + 42); 1599 | let r = get_random_x_bit_bn(LIMB_WIDTH - 47); 1600 | let modulus = get_random_x_bit_bn(16); 1601 | let bn_test_res = (l.clone() * r.clone()) % modulus; 1602 | let test_circuit = TestModPower216MulCircuit { l, r, bn_test_res }; 1603 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1604 | Ok(prover_run) => prover_run, 1605 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1606 | }; 1607 | match prover.verify() { 1608 | Ok(_) => (), 1609 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1610 | }; 1611 | 1612 | let l = BigUint::parse_bytes(b"fffffffffffffffffffffffffff", 16).unwrap(); 1613 | let r = BigUint::from(1u128); 1614 | let modulus = BigUint::from(1u128 << 108) * BigUint::from(1u128 << 108); 1615 | let bn_test_res = (l.clone() * r.clone()) % modulus; 1616 | let test_circuit = TestModPower216MulCircuit { l, r, bn_test_res }; 1617 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1618 | Ok(prover_run) => prover_run, 1619 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1620 | }; 1621 | match prover.verify() { 1622 | Ok(_) => (), 1623 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1624 | }; 1625 | 1626 | Ok(()) 1627 | } 1628 | 1629 | fn run_mod_power108m1_vs_216_circuits() -> Result<(), CircuitError> { 1630 | // product of l and r cannot exceed LIMB_WIDTH bits or mod 108m1 != mod 216. 1631 | // generate a random number up to L_BITLENGTH to use as the seed for the l bit length. 1632 | // i.e., l (1-80-bits) * r (LIMB_WIDTH - (1-80-bits)) < (2^108)-1) 1633 | const L_BITLENGTH: usize = 80; 1634 | let mut rng = rand::thread_rng(); 1635 | let (l, r) = get_random_product_not_exceed_n_bits(rng.gen_range(1..=L_BITLENGTH)); 1636 | let modulus = get_random_x_bit_bn(32); 1637 | let mult = l.clone().mul(r.clone()); 1638 | let quotient = mult.clone().div(modulus.clone()); 1639 | let rem = mult - (quotient.clone() * modulus.clone()); 1640 | let test_circuit = Test108m1v216Circuit { 1641 | l, 1642 | r, 1643 | modulus, 1644 | quotient, 1645 | rem, 1646 | }; 1647 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1648 | Ok(prover_run) => prover_run, 1649 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1650 | }; 1651 | match prover.verify() { 1652 | Ok(_) => (), 1653 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1654 | }; 1655 | Ok(()) 1656 | } 1657 | 1658 | fn run_mod_mult_circuit() -> Result<(), CircuitError> { 1659 | const NUM_TESTS: usize = 6; 1660 | let mut bn_lhs_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1661 | let mut bn_rhs_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1662 | let mut bn_modulus_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1663 | let bit_len_l: [usize; NUM_TESTS] = [ 1664 | 1, 1665 | 4, 1666 | 8, 1667 | LIMB_WIDTH - 1, 1668 | LIMB_WIDTH + 1, 1669 | LIMB_WIDTH + LIMB_WIDTH + 1, 1670 | ]; 1671 | let bit_len_r: [usize; NUM_TESTS] = [ 1672 | 1, 1673 | 4, 1674 | 8, 1675 | LIMB_WIDTH - 1, 1676 | LIMB_WIDTH + 36, 1677 | LIMB_WIDTH + LIMB_WIDTH + 10, 1678 | ]; // + 12 will error 1679 | let bit_len_m: [usize; NUM_TESTS] = [1, 4, 8, 12, 16, 20]; 1680 | 1681 | for i in 0..NUM_TESTS { 1682 | bn_lhs_test_vectors.push(get_random_x_bit_bn(bit_len_l[i])); 1683 | bn_rhs_test_vectors.push(get_random_x_bit_bn(bit_len_r[i])); 1684 | bn_modulus_test_vectors.push(get_random_x_bit_bn(bit_len_m[i])); 1685 | } 1686 | for i in 0..NUM_TESTS { 1687 | let lhs_testcase = bn_lhs_test_vectors[i].clone(); 1688 | let rhs_testcase = bn_rhs_test_vectors[i].clone(); 1689 | let modulus_testcase = bn_modulus_test_vectors[i].clone(); 1690 | 1691 | let bn_test_res = 1692 | (lhs_testcase.clone() * rhs_testcase.clone()) % modulus_testcase.clone(); 1693 | println!( 1694 | "testcase: (0x{})*(0x{}) mod 0x{} = 0x{}", 1695 | lhs_testcase.clone().to_str_radix(16), 1696 | rhs_testcase.clone().to_str_radix(16), 1697 | modulus_testcase.clone().to_str_radix(16), 1698 | bn_test_res.to_str_radix(16) 1699 | ); 1700 | 1701 | let l = lhs_testcase.clone(); 1702 | let r = rhs_testcase.clone(); 1703 | let modulus = modulus_testcase.clone(); 1704 | let test_circuit = TestModMultCircuit { 1705 | l, 1706 | r, 1707 | modulus, 1708 | bn_test_res, 1709 | }; 1710 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1711 | Ok(prover) => prover, 1712 | Err(e) => panic!("{:#?}", e), 1713 | }; 1714 | match prover.verify() { 1715 | Ok(_) => (), 1716 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1717 | }; 1718 | } 1719 | Ok(()) 1720 | } 1721 | 1722 | fn run_mod_mult_circuit_zero_modulus() -> Result<(), CircuitError> { 1723 | const NUM_TESTS: usize = 6; 1724 | let mut bn_lhs_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1725 | let mut bn_rhs_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1726 | let bit_len_l: [usize; NUM_TESTS] = [ 1727 | 1, 1728 | 4, 1729 | 8, 1730 | LIMB_WIDTH - 1, 1731 | LIMB_WIDTH + 1, 1732 | LIMB_WIDTH + LIMB_WIDTH + 1, 1733 | ]; 1734 | let bit_len_r: [usize; NUM_TESTS] = [ 1735 | 1, 1736 | 4, 1737 | 8, 1738 | LIMB_WIDTH - 1, 1739 | LIMB_WIDTH + 36, 1740 | LIMB_WIDTH + LIMB_WIDTH + 10, 1741 | ]; // + 12 will error 1742 | 1743 | for i in 0..NUM_TESTS { 1744 | bn_lhs_test_vectors.push(get_random_x_bit_bn(bit_len_l[i])); 1745 | bn_rhs_test_vectors.push(get_random_x_bit_bn(bit_len_r[i])); 1746 | } 1747 | for i in 0..NUM_TESTS { 1748 | let lhs_testcase = bn_lhs_test_vectors[i].clone(); 1749 | let rhs_testcase = bn_rhs_test_vectors[i].clone(); 1750 | 1751 | let bn_test_res = BigUint::from(0u128); 1752 | println!( 1753 | "testcase: (0x{})*(0x{}) mod 0 = 0x{}", 1754 | lhs_testcase.clone().to_str_radix(16), 1755 | rhs_testcase.clone().to_str_radix(16), 1756 | bn_test_res.to_str_radix(16) 1757 | ); 1758 | 1759 | let l = lhs_testcase.clone(); 1760 | let r = rhs_testcase.clone(); 1761 | let modulus = BigUint::from(0u128); 1762 | 1763 | let test_circuit = TestModMultCircuit { 1764 | l, 1765 | r, 1766 | modulus, 1767 | bn_test_res, 1768 | }; 1769 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1770 | Ok(prover) => prover, 1771 | Err(e) => panic!("{:#?}", e), 1772 | }; 1773 | match prover.verify() { 1774 | Ok(_) => (), 1775 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1776 | }; 1777 | } 1778 | Ok(()) 1779 | } 1780 | 1781 | fn run_modexp_circuit() -> Result<(), CircuitError> { 1782 | // Create an a set of test vectors varying in bit lengths for base, exp & modulus. 1783 | // Test will pass if: 1784 | // (1) result returned from circuit constrain_equal() to the 1785 | // assigned result from bn calculation. 1786 | // (2) prover.verify() for each run verifies without error. 1787 | const NUM_TESTS: usize = 5; 1788 | let mut bn_base_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1789 | let mut bn_modulus_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1790 | let mut bn_exp_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1791 | let bit_len_b: [usize; NUM_TESTS] = [1, 4, 8, 250, 255]; 1792 | let bit_len_m: [usize; NUM_TESTS] = [1, 4, 8, 255, 254]; 1793 | let bit_len_e: [usize; NUM_TESTS] = [ 1794 | //change for larger exp bit length. 1795 | 1, 1796 | LIMB_WIDTH - 1, 1797 | LIMB_WIDTH + 1, 1798 | LIMB_WIDTH + LIMB_WIDTH - 1, 1799 | LIMB_WIDTH + LIMB_WIDTH + LIMB_WIDTH - 90, // max before overflow (need to check range) 1800 | ]; 1801 | for i in 0..NUM_TESTS { 1802 | bn_base_test_vectors.push(get_random_x_bit_bn(bit_len_b[i])); 1803 | bn_modulus_test_vectors.push(get_random_x_bit_bn(bit_len_m[i])); 1804 | bn_exp_test_vectors.push(get_random_x_bit_bn(bit_len_e[i])); 1805 | } 1806 | for i in 0..NUM_TESTS { 1807 | let base_testcase = bn_base_test_vectors[i].clone(); 1808 | let modulus_testcase = bn_modulus_test_vectors[i].clone(); 1809 | let exp_testcase = bn_exp_test_vectors[i].clone(); 1810 | //let bn_test_res = big_pow(base_testcase.clone(), exp_testcase.clone()) % modulus_testcase.clone(); 1811 | let bn_test_res = base_testcase 1812 | .clone() 1813 | .modpow(&exp_testcase, &modulus_testcase); 1814 | println!( 1815 | "testcase: (0x{})^(0x{}) mod 0x{} = 0x{}", 1816 | base_testcase.clone().to_str_radix(16), 1817 | exp_testcase.clone().to_str_radix(16), 1818 | modulus_testcase.clone().to_str_radix(16), 1819 | bn_test_res.to_str_radix(16) 1820 | ); 1821 | let base = base_testcase.clone(); 1822 | let exp = exp_testcase.clone(); 1823 | let modulus = modulus_testcase.clone(); 1824 | let test_circuit = TestModExpCircuit { 1825 | base, 1826 | exp, 1827 | modulus, 1828 | bn_test_res, 1829 | }; 1830 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1831 | Ok(prover_run) => prover_run, 1832 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1833 | }; 1834 | match prover.verify() { 1835 | Ok(_) => (), 1836 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1837 | }; 1838 | } 1839 | Ok(()) 1840 | } 1841 | 1842 | fn run_modexp_zero_modulus_circuit() -> Result<(), CircuitError> { 1843 | // Create an a set of test vectors varying in bit lengths for base, exp & modulus. 1844 | // Test will pass if: 1845 | // (1) result returned from circuit constrain_equal() to the 1846 | // assigned result from bn calculation. 1847 | // (2) prover.verify() for each run verifies without error. 1848 | const NUM_TESTS: usize = 5; 1849 | let mut bn_base_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1850 | let mut bn_exp_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1851 | let bit_len_b: [usize; NUM_TESTS] = [1, 4, 8, 10, 16]; 1852 | let bit_len_e: [usize; NUM_TESTS] = [ 1853 | //change for larger exp bit length. 1854 | 1, 1855 | LIMB_WIDTH - 1, 1856 | LIMB_WIDTH + 1, 1857 | LIMB_WIDTH + LIMB_WIDTH - 1, 1858 | LIMB_WIDTH + LIMB_WIDTH + LIMB_WIDTH - 90, // max before overflow (need to check range) 1859 | ]; 1860 | for i in 0..NUM_TESTS { 1861 | bn_base_test_vectors.push(get_random_x_bit_bn(bit_len_b[i])); 1862 | bn_exp_test_vectors.push(get_random_x_bit_bn(bit_len_e[i])); 1863 | } 1864 | for i in 0..NUM_TESTS { 1865 | let base_testcase: BigUint = bn_base_test_vectors[i].clone(); 1866 | let exp_testcase = bn_exp_test_vectors[i].clone(); 1867 | //let bn_test_res = big_pow(base_testcase.clone(), exp_testcase.clone()) % modulus_testcase.clone(); 1868 | let bn_test_res = BigUint::from(0u128); 1869 | println!( 1870 | "testcase: (0x{})^(0x{}) mod 0 = 0x{}", 1871 | base_testcase.clone().to_str_radix(16), 1872 | exp_testcase.clone().to_str_radix(16), 1873 | bn_test_res.to_str_radix(16) 1874 | ); 1875 | let base = base_testcase.clone(); 1876 | let exp = exp_testcase.clone(); 1877 | let modulus = BigUint::from(0u128); 1878 | 1879 | let test_circuit = TestModExpCircuit { 1880 | base, 1881 | exp, 1882 | modulus, 1883 | bn_test_res, 1884 | }; 1885 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1886 | Ok(prover_run) => prover_run, 1887 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1888 | }; 1889 | match prover.verify() { 1890 | Ok(_) => (), 1891 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1892 | }; 1893 | } 1894 | Ok(()) 1895 | } 1896 | 1897 | fn run_modexp_small_p_circuit() -> Result<(), CircuitError> { 1898 | // Create an a set of test vectors varying in bit lengths for base, exp & modulus. 1899 | // Test will pass if: 1900 | // (1) result returned from circuit constrain_equal() to the 1901 | // assigned result from bn calculation. 1902 | // (2) prover.verify() for each run verifies without error. 1903 | const NUM_TESTS: usize = 1; 1904 | let mut bn_base_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1905 | let mut bn_modulus_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1906 | let mut bn_exp_test_vectors: Vec = Vec::with_capacity(NUM_TESTS); 1907 | for _ in 0..NUM_TESTS { 1908 | let u256_from_str = 1909 | BigUint::parse_bytes("efffffffffffffffffffffffffffffee".as_bytes(), 16).unwrap(); 1910 | bn_base_test_vectors.push(u256_from_str); 1911 | bn_modulus_test_vectors.push(BigUint::from(2u64)); 1912 | bn_exp_test_vectors.push(BigUint::from(3u64)); 1913 | } 1914 | for i in 0..NUM_TESTS { 1915 | let base_testcase = bn_base_test_vectors[i].clone(); 1916 | let modulus_testcase = bn_modulus_test_vectors[i].clone(); 1917 | println!("modulus_testcase is {}", modulus_testcase); 1918 | 1919 | let exp_testcase = bn_exp_test_vectors[i].clone(); 1920 | //let bn_test_res = big_pow(base_testcase.clone(), exp_testcase.clone()) % modulus_testcase.clone(); 1921 | let bn_test_res = base_testcase 1922 | .clone() 1923 | .modpow(&exp_testcase, &modulus_testcase); 1924 | println!( 1925 | "testcase: (0x{})^(0x{}) mod 0x{} = 0x{}", 1926 | base_testcase.clone().to_str_radix(16), 1927 | exp_testcase.clone().to_str_radix(16), 1928 | modulus_testcase.clone().to_str_radix(16), 1929 | bn_test_res.to_str_radix(16) 1930 | ); 1931 | let base = base_testcase.clone(); 1932 | let exp = exp_testcase.clone(); 1933 | let modulus = modulus_testcase.clone(); 1934 | let test_circuit = TestModExpCircuit { 1935 | base, 1936 | exp, 1937 | modulus, 1938 | bn_test_res, 1939 | }; 1940 | let prover = match MockProver::run(16, &test_circuit, vec![]) { 1941 | Ok(prover_run) => prover_run, 1942 | Err(prover_error) => return Err(CircuitError::ProverError(prover_error)), 1943 | }; 1944 | match prover.verify() { 1945 | Ok(_) => (), 1946 | Err(verifier_error) => return Err(CircuitError::VerifierError(verifier_error)), 1947 | }; 1948 | } 1949 | Ok(()) 1950 | } 1951 | 1952 | #[test] 1953 | fn test_mod_power108m1_only() { 1954 | let output = 1955 | run_mod_power108m1_circuit().expect("\nmod_power108m1_circuit failed prover verify"); 1956 | println!("\nproof generation successful!\nresult: {:#?}", output); 1957 | } 1958 | 1959 | #[test] 1960 | fn test_mod_power108m1_zero_only() { 1961 | let output = 1962 | run_mod_power108m1_circuit().expect("\nmod_power108m1_circuit failed prover verify"); 1963 | println!("\nproof generation successful!\nresult: {:#?}", output); 1964 | } 1965 | 1966 | #[test] 1967 | fn test_mod_power108m1_mul() { 1968 | let output = 1969 | run_mod_power108m1_mul_circuit().expect("\nmod_power108m1_mul failed prover verify"); 1970 | println!("\nproof generation successful!\nresult: {:#?}", output); 1971 | } 1972 | 1973 | #[test] 1974 | fn test_mod_power216_mul() { 1975 | let output = 1976 | run_mod_power216_mul_circuit().expect("\nmod_mult_circuit failed prover verify"); 1977 | println!("\nproof generation successful!\nresult: {:#?}", output); 1978 | } 1979 | 1980 | #[test] 1981 | fn test_mod_108m1_vs_216() { 1982 | let output = run_mod_power108m1_vs_216_circuits() 1983 | .expect("\nmod_108m1_vs_216_circuit failed prover verify"); 1984 | println!("\nproof generation successful!\nresult: {:#?}", output); 1985 | } 1986 | 1987 | #[test] 1988 | fn test_mod_mult() { 1989 | let output = run_mod_mult_circuit().expect("\nmod_mult_circuit failed prover verify"); 1990 | println!("\nproof generation successful!\nresult: {:#?}", output); 1991 | } 1992 | 1993 | #[test] 1994 | fn test_mod_mult_zero_modulus() { 1995 | let output = 1996 | run_mod_mult_circuit_zero_modulus().expect("\nmod_mult_circuit failed prover verify"); 1997 | println!("\nproof generation successful!\nresult: {:#?}", output); 1998 | } 1999 | 2000 | #[test] 2001 | fn test_modexp() { 2002 | let output = run_modexp_circuit().expect("\nmodexp_circuit failed prover verify"); 2003 | println!("\nproof generation successful!\nresult: {:#?}", output); 2004 | } 2005 | 2006 | #[test] 2007 | fn test_modexp_zero_modulus() { 2008 | let output = 2009 | run_modexp_zero_modulus_circuit().expect("\nmodexp_circuit failed prover verify"); 2010 | println!("\nproof generation successful!\nresult: {:#?}", output); 2011 | } 2012 | 2013 | #[test] 2014 | fn test_modexp_small_p() { 2015 | let output = run_modexp_small_p_circuit().expect("\nmodexp_circuit failed prover verify"); 2016 | println!("\nproof generation successful!\nresult: {:#?}", output); 2017 | } 2018 | 2019 | // test helpers: 2020 | use halo2_proofs::dev::VerifyFailure; 2021 | use std::fmt; 2022 | 2023 | pub enum CircuitError { 2024 | /// Thrown when `MockProver::run` fails to prove the circuit. 2025 | ProverError(Error), 2026 | /// Thrown when verification fails. 2027 | VerifierError(Vec), 2028 | } 2029 | 2030 | impl fmt::Debug for CircuitError { 2031 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 2032 | match self { 2033 | CircuitError::ProverError(prover_error) => { 2034 | write!(f, "prover error in circuit: {}", prover_error) 2035 | } 2036 | CircuitError::VerifierError(verifier_error) => { 2037 | write!(f, "verifier error in circuit: {:#?}", verifier_error) 2038 | } 2039 | } 2040 | } 2041 | } 2042 | 2043 | /// # bn_limbs from BigUint 2044 | /// Extracts BigUint to tuple of limbs \ 2045 | /// BigUint -> (l2,l1,l0) 2046 | /// 2047 | fn get_limbs_from_bn(bn: &BigUint) -> (BigUint, BigUint, BigUint) { 2048 | let limb0 = bn.modpow(&BigUint::from(1u128), &BigUint::from(1u128 << 108)); 2049 | let limb1 = ((bn - limb0.clone()) / BigUint::from(1u128 << 108)) 2050 | .modpow(&BigUint::from(1u128), &BigUint::from(1u128 << 108)); 2051 | let limb2 = (bn / BigUint::from(1u128 << 108)) / (BigUint::from(1u128 << 108)); 2052 | (limb2, limb1, limb0) 2053 | } 2054 | 2055 | /// # random BigUint x-bits long 2056 | /// returns a BigUint with bit length = bit_length 2057 | fn get_random_x_bit_bn(bit_length: usize) -> BigUint { 2058 | let mut rng = thread_rng(); 2059 | let mut b = BigUint::default(); 2060 | while b.bits() != bit_length as u64 { 2061 | b = rng.sample(RandomBits::new(bit_length as u64)); 2062 | } 2063 | b 2064 | } 2065 | 2066 | /// # Returns two BigUint values whose product does not exceed LIMB_WIDTH bits. 2067 | fn get_random_product_not_exceed_n_bits(n: usize) -> (BigUint, BigUint) { 2068 | let vmax = BigUint::parse_bytes(b"fffffffffffffffffffffffffff", 16).unwrap(); 2069 | let b1 = get_random_x_bit_bn(n); 2070 | let max_bits = LIMB_WIDTH - b1.bits() as usize; 2071 | let mut b2 = vmax.clone(); 2072 | while (b2.clone() * b1.clone()) > vmax { 2073 | b2 = get_random_x_bit_bn(max_bits); 2074 | } 2075 | (b1, b2) 2076 | } 2077 | } 2078 | --------------------------------------------------------------------------------