Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ECC chip] Fixed- and variable-base scalar multiplication #111

Merged
merged 40 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0f60a81
ecc::chip.rs: Add EccScalarFixed, EccScalarFixedShort structs
therealyingtong Jun 12, 2021
64a2b02
ecc::chip.rs: Witness scalar for variable-base scalar mul
therealyingtong Jun 12, 2021
cc9dd20
chip::mul.rs: Implement variable-base scalar mul instruction.
therealyingtong Jun 5, 2021
a263774
chip::witness_scalar_fixed.rs: Implement witness_scalar_fixed instruc…
therealyingtong Jun 5, 2021
ae25310
chip::mul_fixed.rs: Implement fixed-base scalar mul instruction.
therealyingtong Jun 5, 2021
a3ca27b
ecc::tests: Add tests for variable- and fixed-base scalar mul.
therealyingtong Jun 14, 2021
5ae9890
mul::overflow.rs: Overflow check in variable-base scalar mul
therealyingtong Jun 13, 2021
747f71c
constants.rs: Add unit tests for T_P, T_Q constants.
therealyingtong Jun 15, 2021
37074c6
mul_fixed::short: Check that last window is either 0 or 1.
therealyingtong Jun 18, 2021
09b4da1
base_field_elem.rs: Support fixed-base mul using base field element.
therealyingtong Jun 18, 2021
b15343f
Add `OrchardFixedBasesFull::{generator, u}` methods
str4d Jun 16, 2021
e726fee
mul_fixed: Avoid computing fixed constants during proving
str4d Jun 16, 2021
69d6629
chip::mul.rs: Enforce LSB if/else condition
therealyingtong Jun 18, 2021
b363492
ecc::chip.rs: Introduce circuit-wide "constants" fixed column
therealyingtong Jun 19, 2021
4d69dec
mul::incomplete.rs: Constrain first and last y_a values.
therealyingtong Jul 2, 2021
e75c176
mul::incomplete.rs: Make offsets more intuitive
therealyingtong Jul 2, 2021
6ffd867
mul::complete.rs: Constrain negation of (x_p, y_p) in double-and-add.
therealyingtong Jul 2, 2021
3f961ab
mul::process_lsb(): Clean up assignments and boolean-constrain LSB.
therealyingtong Jul 2, 2021
33b66ab
tests::print_ecc_chip(): Print ECC chip.
therealyingtong Jul 2, 2021
67caed5
mul::incomplete: Constrain final iteration correctly
str4d Jul 2, 2021
2536555
mul_fixed: Constrain interpolated window mul to be on curve.
therealyingtong Jul 3, 2021
9fd4d7d
Cleanups and clippy fixes.
therealyingtong Jul 3, 2021
d550e15
mul_fixed_*::tests: Constrain zero outputs in mul_fixed tests.
therealyingtong Jul 3, 2021
2d343af
Update mul_fixed_* APIs to take Layouter instead of Region.
therealyingtong Jul 7, 2021
23f2ed5
gadget::utilities.rs: Add bitrange_subset() helper.
therealyingtong Jul 7, 2021
b690940
chip::mul_fixed.rs: Make q_mul_fixed a selector instead of fixed column.
therealyingtong Jul 7, 2021
72e469e
mul_fixed::base_field_elem.rs: Check canonicity of base field element…
therealyingtong Jul 7, 2021
ae72501
mul_fixed::base_field_elem: Add constraint alpha_2 = 0 => alpha_1 = 0.
therealyingtong Jul 7, 2021
f42d48b
mul_fixed::base_field_elem: Fix two_pow_130 expression.
therealyingtong Jul 8, 2021
d0e34cd
mul_fixed::base_field_elem: Eliminate alpha_0 lookup decomposition.
therealyingtong Jul 8, 2021
96863c9
mul_fixed::*: Use a separate region for complete addition assignment.
therealyingtong Jul 8, 2021
22ec16f
Minor refactors, cleanups, clippy fixes, docfixes.
therealyingtong Jul 8, 2021
e2ea443
mul_fixed::*::tests: Witness expected point and constrain result to b…
therealyingtong Jul 8, 2021
5c38f53
mul::tests: Witness expected point and constrain result to be equal.
therealyingtong Jul 8, 2021
ae4e54d
gadget::utilities: Add test cases for bitrange_subset() helper.
therealyingtong Jul 8, 2021
8a9f821
mul_fixed::base_field_elem: Remove double-enable of base_field_fixed_…
therealyingtong Jul 9, 2021
6c41c72
utilities::range_check: Correct range_check expression
therealyingtong Jul 9, 2021
0ade539
utilities::tests::test_range_check(): Test range_check() helper.
therealyingtong Jul 9, 2021
b696163
mul.rs: Explain ordering of mul::incomplete advice columns.
therealyingtong Jul 14, 2021
425ee6e
Docfixes and minor refactors.
therealyingtong Jul 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/circuit/gadget/ecc/chip/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ impl Config {
.collect()
}

pub(crate) fn output_columns(&self) -> HashSet<Column<Advice>> {
core::array::IntoIter::new([self.x_qr, self.y_qr]).collect()
}

pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
meta.create_gate("complete addition gates", |meta| {
let q_add = meta.query_selector(self.q_add);
Expand Down
26 changes: 16 additions & 10 deletions src/circuit/gadget/ecc/chip/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,20 @@ impl From<&EccConfig> for Config {
"y_p is shared across hi and lo halves."
);

let add_config_advices = config.add_config.advice_columns();
assert!(
!add_config_advices.contains(&config.hi_config.z),
"hi_config z cannot overlap with complete addition columns."
);
assert!(
!add_config_advices.contains(&config.complete_config.z_complete),
"complete_config z cannot overlap with complete addition columns."
);
// For both hi_config and lo_config:
// z and lambda1 are assigned on the same row as the add_config output.
// Therefore, z and lambda1 must not overlap with add_config.x_qr, add_config.y_qr.
let add_config_outputs = config.add_config.output_columns();
for config in [&(*config.hi_config), &(*config.lo_config)].iter() {
assert!(
!add_config_outputs.contains(&config.z),
"incomplete config z cannot overlap with complete addition columns."
);
assert!(
!add_config_outputs.contains(&config.lambda1),
"incomplete config lambda1 cannot overlap with complete addition columns."
);
}

config
}
Expand Down Expand Up @@ -464,7 +469,8 @@ pub mod tests {
scalar_val: pallas::Base,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
// Case scalar from base field into scalar field
// Move scalar from base field into scalar field (which always fits
// for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new(
chip,
Expand Down
9 changes: 3 additions & 6 deletions src/circuit/gadget/ecc/chip/mul_fixed/base_field_elem.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::super::{EccBaseFieldElemFixed, EccConfig, EccPoint, OrchardFixedBasesFull};
use super::H_BASE;

use crate::{
circuit::gadget::utilities::{
Expand Down Expand Up @@ -167,7 +168,7 @@ impl Config {
z_44_alpha.clone() - z_84_alpha * two_pow_120
};
// a_43 = z_43 - (2^3)z_44
let a_43 = z_43_alpha - z_44_alpha * pallas::Base::from_u64(1 << 3);
let a_43 = z_43_alpha - z_44_alpha * *H_BASE;

std::iter::empty()
.chain(Some(("MSB = 1 => alpha_1 = 0", alpha_2.clone() * alpha_1)))
Expand Down Expand Up @@ -456,10 +457,6 @@ impl Config {
&self.super_config.perm,
)?;

for idx in 0..words.len() {
self.base_field_fixed_mul.enable(region, offset + idx)?;
}

let offset = offset + 1;

let eight_inv = pallas::Base::TWO_INV.square() * pallas::Base::TWO_INV;
Expand Down Expand Up @@ -581,7 +578,7 @@ pub mod tests {
scalar_val: pallas::Base,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
// Case scalar from base field into scalar field
// Move scalar from base field into scalar field (which always fits for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new(
chip,
Expand Down
90 changes: 86 additions & 4 deletions src/circuit/gadget/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,101 @@ pub fn bitrange_subset<F: FieldExt + PrimeFieldBits>(field_elem: F, bitrange: Ra
/// Check that an expression is in the small range [0..range),
/// i.e. 0 ≤ word < range.
pub fn range_check<F: FieldExt>(word: Expression<F>, range: usize) -> Expression<F> {
(0..range)
.map(|i| Expression::Constant(F::from_u64(i as u64)))
.reduce(|acc, i| acc * (word.clone() - i))
.expect("range > 0")
(1..range).fold(word.clone(), |acc, i| {
acc * (word.clone() - Expression::Constant(F::from_u64(i as u64)))
})
}

#[cfg(test)]
mod tests {
use super::*;
use bigint::U256;
use ff::PrimeField;
use halo2::{
circuit::{layouter::SingleChipLayouter, Layouter},
dev::{MockProver, VerifyFailure},
plonk::{Assignment, Circuit, ConstraintSystem, Error, Selector},
poly::Rotation,
};
use pasta_curves::pallas;

#[test]
fn test_range_check() {
struct MyCircuit<const RANGE: usize>(u8);

impl<const RANGE: usize> UtilitiesInstructions<pallas::Base> for MyCircuit<RANGE> {
type Var = CellValue<pallas::Base>;
}

#[derive(Clone)]
struct Config {
selector: Selector,
advice: Column<Advice>,
}

impl<const RANGE: usize> Circuit<pallas::Base> for MyCircuit<RANGE> {
type Config = Config;

fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
let selector = meta.selector();
let advice = meta.advice_column();

meta.create_gate("range check", |meta| {
let selector = meta.query_selector(selector);
let advice = meta.query_advice(advice, Rotation::cur());

vec![selector * range_check(advice, RANGE)]
});

Config { selector, advice }
}

fn synthesize(
&self,
cs: &mut impl Assignment<pallas::Base>,
config: Self::Config,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;

layouter.assign_region(
|| "range constrain",
|mut region| {
config.selector.enable(&mut region, 0)?;
region.assign_advice(
|| format!("witness {}", self.0),
config.advice,
0,
|| Ok(pallas::Base::from_u64(self.0.into())),
)?;

Ok(())
},
)
}
}

for i in 0..8 {
let circuit: MyCircuit<8> = MyCircuit(i);
let prover = MockProver::<pallas::Base>::run(1, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()));
}

{
let circuit: MyCircuit<8> = MyCircuit(8);
let prover = MockProver::<pallas::Base>::run(1, &circuit, vec![]).unwrap();
assert_eq!(
prover.verify(),
Err(vec![VerifyFailure::Constraint {
gate_index: 0,
gate_name: "range check",
constraint_index: 0,
constraint_name: "",
row: 0
}])
);
}
}

#[test]
fn test_bitrange_subset() {
// Subset full range.
Expand Down