diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2d24b246ce..21896f49de 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,28 +1,23 @@ name: coverage -on: [push] +on: [pull_request, push] + jobs: - test: - name: coverage + coverage: runs-on: ubuntu-latest - container: - image: xd009642/tarpaulin:0.30.0 - options: --security-opt seccomp=unconfined + env: + CARGO_TERM_COLOR: always steps: - - name: Checkout repository - uses: actions/checkout@v2 - + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov - name: Generate code coverage - run: | - cargo tarpaulin --tests --examples --verbose --all-features --workspace --timeout 120 --out xml - - - name: Workaround for codecov/feedback#263 - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - - - name: Upload to codecov.io - uses: codecov/codecov-action@v4.0.1 + run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true - \ No newline at end of file + token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + files: lcov.info + fail_ci_if_error: true \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..ec65ebc105 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +ignore: + - halo2_proofs/benches + - halo2_proofs/examples + - halo2_proofs/tests + - halo2_frontend/src/dev/graph + - halo2_frontend/src/dev/graph.rs + - halo2_frontend/src/dev/costs.rs + - halo2_frontend/src/dev/cost_model.rs \ No newline at end of file diff --git a/halo2_frontend/src/circuit/value.rs b/halo2_frontend/src/circuit/value.rs index e0426f6bfa..a169d60e41 100644 --- a/halo2_frontend/src/circuit/value.rs +++ b/halo2_frontend/src/circuit/value.rs @@ -25,6 +25,7 @@ impl Default for Value { impl Value { /// Constructs an unwitnessed value. + #[must_use] pub const fn unknown() -> Self { Self { inner: None } } @@ -38,6 +39,7 @@ impl Value { /// /// let v = Value::known(37); /// ``` + #[must_use] pub const fn known(value: V) -> Self { Self { inner: Some(value) } } @@ -97,6 +99,7 @@ impl Value { /// Returns [`Value::unknown()`] if the value is [`Value::unknown()`], otherwise calls /// `f` with the wrapped value and returns the result. + #[must_use] pub fn and_then Value>(self, f: F) -> Value { match self.inner { Some(v) => f(v), @@ -108,6 +111,7 @@ impl Value { /// /// If `self` is `Value::known(s)` and `other` is `Value::known(o)`, this method /// returns `Value::known((s, o))`. Otherwise, [`Value::unknown()`] is returned. + #[must_use] pub fn zip(self, other: Value) -> Value<(V, W)> { Value { inner: self.inner.zip(other.inner), @@ -121,6 +125,7 @@ impl Value<(V, W)> { /// If `self` is `Value::known((a, b)), this method returns /// `(Value::known(a), Value::known(b))`. Otherwise, /// `(Value::unknown(), Value::unknown())` is returned. + #[must_use] pub fn unzip(self) -> (Value, Value) { match self.inner { Some((a, b)) => (Value::known(a), Value::known(b)), @@ -181,6 +186,7 @@ impl Value<[V; LEN]> { /// Transposes a `Value<[V; LEN]>` into a `[Value; LEN]`. /// /// [`Value::unknown()`] will be mapped to `[Value::unknown(); LEN]`. + #[must_use] pub fn transpose_array(self) -> [Value; LEN] { let mut ret = [Value::unknown(); LEN]; if let Some(arr) = self.inner { @@ -204,6 +210,7 @@ where /// # Panics /// /// Panics if `self` is `Value::known(values)` and `values.len() != length`. + #[must_use] pub fn transpose_vec(self, length: usize) -> Vec> { match self.inner { Some(values) => { @@ -225,6 +232,7 @@ impl> FromIterator> for Value { /// elements are taken, and the [`Value::unknown()`] is returned. Should no /// [`Value::unknown()`] occur, a container of type `V` containing the values of each /// [`Value`] is returned. + #[must_use] fn from_iter>>(iter: I) -> Self { Self { inner: iter.into_iter().map(|v| v.inner).collect(), @@ -239,6 +247,7 @@ impl> FromIterator> for Value { impl Neg for Value { type Output = Value; + #[must_use] fn neg(self) -> Self::Output { Value { inner: self.inner.map(|v| -v), @@ -256,6 +265,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: Self) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b), @@ -269,6 +279,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: Self) -> Self::Output { Value { inner: self @@ -286,6 +297,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: Value<&V>) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b), @@ -299,6 +311,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b), @@ -312,6 +325,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: &Self) -> Self::Output { self + rhs.as_ref() } @@ -323,6 +337,7 @@ where { type Output = Value; + #[must_use] fn add(self, rhs: Value) -> Self::Output { self.as_ref() + rhs } @@ -338,6 +353,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: Self) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b), @@ -351,6 +367,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: Self) -> Self::Output { Value { inner: self @@ -368,6 +385,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: Value<&V>) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b), @@ -381,6 +399,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b), @@ -394,6 +413,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: &Self) -> Self::Output { self - rhs.as_ref() } @@ -405,6 +425,7 @@ where { type Output = Value; + #[must_use] fn sub(self, rhs: Value) -> Self::Output { self.as_ref() - rhs } @@ -420,6 +441,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: Self) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b), @@ -433,6 +455,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: Self) -> Self::Output { Value { inner: self @@ -450,6 +473,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: Value<&V>) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b), @@ -463,6 +487,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b), @@ -476,6 +501,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: &Self) -> Self::Output { self * rhs.as_ref() } @@ -487,6 +513,7 @@ where { type Output = Value; + #[must_use] fn mul(self, rhs: Value) -> Self::Output { self.as_ref() * rhs } @@ -497,6 +524,7 @@ where // impl From> for Value> { + #[must_use] fn from(value: Value) -> Self { Self { inner: value.inner.map(Assigned::from), @@ -507,6 +535,7 @@ impl From> for Value> { impl Add> for Value> { type Output = Value>; + #[must_use] fn add(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b), @@ -517,6 +546,7 @@ impl Add> for Value> { impl Add for Value> { type Output = Value>; + #[must_use] fn add(self, rhs: F) -> Self::Output { self + Value::known(rhs) } @@ -525,6 +555,7 @@ impl Add for Value> { impl Add> for Value<&Assigned> { type Output = Value>; + #[must_use] fn add(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b), @@ -535,6 +566,7 @@ impl Add> for Value<&Assigned> { impl Add for Value<&Assigned> { type Output = Value>; + #[must_use] fn add(self, rhs: F) -> Self::Output { self + Value::known(rhs) } @@ -543,6 +575,7 @@ impl Add for Value<&Assigned> { impl Sub> for Value> { type Output = Value>; + #[must_use] fn sub(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b), @@ -553,6 +586,7 @@ impl Sub> for Value> { impl Sub for Value> { type Output = Value>; + #[must_use] fn sub(self, rhs: F) -> Self::Output { self - Value::known(rhs) } @@ -561,6 +595,7 @@ impl Sub for Value> { impl Sub> for Value<&Assigned> { type Output = Value>; + #[must_use] fn sub(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b), @@ -571,6 +606,7 @@ impl Sub> for Value<&Assigned> { impl Sub for Value<&Assigned> { type Output = Value>; + #[must_use] fn sub(self, rhs: F) -> Self::Output { self - Value::known(rhs) } @@ -579,6 +615,7 @@ impl Sub for Value<&Assigned> { impl Mul> for Value> { type Output = Value>; + #[must_use] fn mul(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b), @@ -589,6 +626,7 @@ impl Mul> for Value> { impl Mul for Value> { type Output = Value>; + #[must_use] fn mul(self, rhs: F) -> Self::Output { self * Value::known(rhs) } @@ -597,6 +635,7 @@ impl Mul for Value> { impl Mul> for Value<&Assigned> { type Output = Value>; + #[must_use] fn mul(self, rhs: Value) -> Self::Output { Value { inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b), @@ -607,6 +646,7 @@ impl Mul> for Value<&Assigned> { impl Mul for Value<&Assigned> { type Output = Value>; + #[must_use] fn mul(self, rhs: F) -> Self::Output { self * Value::known(rhs) } @@ -614,6 +654,7 @@ impl Mul for Value<&Assigned> { impl Value { /// Returns the field element corresponding to this value. + #[must_use] pub fn to_field(&self) -> Value> where for<'v> Assigned: From<&'v V>, @@ -624,6 +665,7 @@ impl Value { } /// Returns the field element corresponding to this value. + #[must_use] pub fn into_field(self) -> Value> where V: Into>, @@ -645,8 +687,9 @@ impl Value { /// /// let v = Value::known(F::from(2)); /// let v: Value> = v.into(); - /// v.double(); + /// let _ = v.double(); /// ``` + #[must_use] pub fn double(&self) -> Value> where V: Borrow>, @@ -657,6 +700,7 @@ impl Value { } /// Squares this field element. + #[must_use] pub fn square(&self) -> Value> where V: Borrow>, @@ -667,6 +711,7 @@ impl Value { } /// Cubes this field element. + #[must_use] pub fn cube(&self) -> Value> where V: Borrow>, @@ -677,6 +722,7 @@ impl Value { } /// Inverts this assigned value (taking the inverse of zero to be zero). + #[must_use] pub fn invert(&self) -> Value> where V: Borrow>, @@ -691,9 +737,246 @@ impl Value> { /// Evaluates this value directly, performing an unbatched inversion if necessary. /// /// If the denominator is zero, the returned value is zero. + #[must_use] pub fn evaluate(self) -> Value { Value { inner: self.inner.map(|v| v.evaluate()), } } } + +#[cfg(test)] +mod test { + #![allow(clippy::op_ref)] + + use super::*; + use halo2curves::bn256::Fr; + + type V = Value; + + impl PartialEq for V { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } + } + impl PartialEq for Value> { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } + } + + #[test] + fn test_value_as_mut() { + let mut v_some = V::known(1); + let mut v_none = V::default(); + v_some.as_mut().map(|v| *v = 3); + v_none.as_mut().map(|v| *v = 3); + assert_eq!(v_some, V::known(3)); + assert_eq!(v_none, V::unknown()); + } + + #[test] + fn test_value_assert_if_known_ok() { + V::known(1).assert_if_known(|v| *v == 1); + V::unknown().assert_if_known(|v| *v == 1); + } + + #[test] + #[should_panic] + fn test_value_assert_if_known_ko() { + V::known(1).assert_if_known(|v| *v == 2); + } + + #[test] + fn test_value_error_if_known() { + assert!(V::known(1).error_if_known_and(|v| *v == 1).is_err()); + assert!(V::known(1).error_if_known_and(|v| *v == 2).is_ok()); + assert!(V::unknown().error_if_known_and(|_| true).is_ok()); + } + + #[test] + fn test_map() { + assert_eq!(V::known(1).map(|v| v + 1), V::known(2)); + assert_eq!(V::unknown().map(|v| v + 1), V::unknown()); + } + + #[test] + fn test_value_and_then() { + let v = V::known(1); + assert_eq!(v.and_then(|v| V::known(v + 1)), V::known(2)); + assert_eq!(v.and_then(|_| V::unknown()), V::unknown()); + assert_eq!(V::unknown().and_then(|v| V::known(v + 1)), V::unknown()); + } + + #[test] + fn test_value_zip() { + assert_eq!( + V::known(1).zip(V::known(2)).unzip(), + (V::known(1), V::known(2)) + ); + assert_eq!( + V::known(1).zip(V::unknown()).unzip(), + (V::unknown(), V::unknown()) + ); + assert_eq!( + V::unknown().zip(V::known(2)).unzip(), + (Value::unknown(), V::unknown()) + ); + assert_eq!( + V::unknown().zip(V::unknown()).unzip(), + (Value::unknown(), V::unknown()) + ); + } + + #[test] + fn test_value_copies() { + let copy = Value::<&mut i64>::known(&mut 1).copied(); + let clon = Value::<&mut i64>::known(&mut 1).cloned(); + assert_eq!(copy, Value::known(1)); + assert_eq!(clon, Value::known(1)); + } + + #[test] + fn test_value_transpose_array() { + assert_eq!( + Value::<[_; 2]>::known([1, 2]).transpose_array(), + [V::known(1), V::known(2)] + ); + } + + #[test] + fn test_value_transpose_vec_ok() { + assert_eq!( + Value::<[_; 2]>::known([1, 2]).transpose_vec(2), + vec![V::known(1), V::known(2)] + ); + assert_eq!( + Value::<[_; 2]>::unknown().transpose_vec(2), + vec![V::unknown(), V::unknown()] + ); + + // TODO: check if should be this allowed or not + assert_eq!( + Value::<[_; 6]>::unknown().transpose_vec(2), + vec![V::unknown(), V::unknown()] + ); + } + + #[test] + #[should_panic] + fn test_value_transpose_vec_ko_1() { + assert_eq!( + Value::<[_; 2]>::known([1, 2]).transpose_vec(1), + vec![V::known(1), V::known(2)] + ); + } + + #[test] + #[should_panic] + fn test_value_transpose_vec_ko_2() { + assert_eq!( + Value::<[_; 2]>::known([1, 2]).transpose_vec(3), + vec![V::known(1), V::known(2)] + ); + } + + #[test] + fn test_value_from_iter() { + assert_eq!( + Value::>::from_iter([V::known(1), V::known(2)]).inner, + Some(vec![1, 2]) + ); + assert_eq!( + Value::>::from_iter([V::known(1), V::unknown()]).inner, + None + ); + } + + #[test] + fn test_value_ops() { + assert_eq!(-V::known(5), Value::known(-5)); + + assert_eq!(V::known(5) + V::known(2), V::known(7)); + assert_eq!(&V::known(5) + V::known(2), V::known(7)); + assert_eq!(V::known(5) + &V::known(2), V::known(7)); + assert_eq!(&V::known(5) + &V::known(2), V::known(7)); + + assert_eq!(V::known(5) - V::known(2), V::known(3)); + assert_eq!(&V::known(5) - V::known(2), V::known(3)); + assert_eq!(V::known(5) - &V::known(2), V::known(3)); + assert_eq!(&V::known(5) - &V::known(2), V::known(3)); + + assert_eq!(V::known(5) * V::known(2), V::known(10)); + assert_eq!(&V::known(5) * V::known(2), V::known(10)); + assert_eq!(V::known(5) * &V::known(2), V::known(10)); + assert_eq!(&V::known(5) * &V::known(2), V::known(10)); + } + + #[test] + fn test_value_assigned() { + let fr_two = || Fr::from(2); + let fr_three = || Fr::from(3); + + let one = Value::known(Assigned::Trivial(Fr::one())); + let two = Value::known(Assigned::Trivial(Fr::from(2))); + let six = Value::known(Assigned::Trivial(Fr::from(6))); + + let v: Value> = Value::known(Fr::one()).into(); + assert_eq!(v, Value::known(Assigned::Trivial(Fr::one()))); + + assert_eq!(one + Fr::one(), two); + assert_eq!(one + Value::known(Fr::one()), two); + assert_eq!( + Value::known(&Assigned::Trivial(Fr::one())) + Value::known(Fr::one()), + two + ); + assert_eq!(Value::known(&Assigned::Trivial(Fr::one())) + Fr::one(), two); + + assert_eq!(two - Value::known(Fr::one()), one); + assert_eq!(two - Fr::one(), one); + assert_eq!( + Value::known(&Assigned::Trivial(fr_two())) - Value::known(Fr::one()), + one + ); + assert_eq!(Value::known(&Assigned::Trivial(fr_two())) - Fr::one(), one); + + assert_eq!(two * Value::known(fr_three()), six); + assert_eq!(two * fr_three(), six); + assert_eq!( + Value::known(&Assigned::Trivial(fr_two())) * Value::known(fr_three()), + six + ); + assert_eq!(Value::known(&Assigned::Trivial(fr_two())) * fr_three(), six); + } + + #[test] + fn test_value_impl() { + assert_eq!( + Value::known(Fr::one()).to_field(), + Value::known(Assigned::Trivial(Fr::one())) + ); + assert_eq!( + Value::known(Fr::one()).into_field(), + Value::known(Assigned::Trivial(Fr::one())) + ); + + assert_eq!( + Value::known(Assigned::Trivial(Fr::from(3))).double(), + Value::known(Assigned::Trivial(Fr::from(6))) + ); + assert_eq!( + Value::known(Assigned::Trivial(Fr::from(3))).square(), + Value::known(Assigned::Trivial(Fr::from(9))) + ); + assert_eq!( + Value::known(Assigned::Trivial(Fr::from(3))).cube(), + Value::known(Assigned::Trivial(Fr::from(27))) + ); + assert_eq!( + Value::known(Assigned::Trivial(Fr::from(3))) + .invert() + .invert(), + Value::known(Assigned::Trivial(Fr::from(3))) + ); + } +} diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index c1a620593f..5ba0fcd3e5 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -505,7 +505,7 @@ const WIDTH_FACTOR: usize = 1; #[test] fn test_mycircuit_full_legacy() { - #[cfg(all(feature = "heap-profiling", not(tarpaulin)))] + #[cfg(all(feature = "heap-profiling", not(coverage)))] let _profiler = dhat::Profiler::new_heap(); use halo2_proofs::plonk::{ @@ -566,7 +566,7 @@ fn test_mycircuit_full_legacy() { fn test_mycircuit_full_split() { use halo2_middleware::zal::impls::{H2cEngine, PlonkEngineConfig}; - #[cfg(all(feature = "heap-profiling", not(tarpaulin)))] + #[cfg(all(feature = "heap-profiling", not(coverage)))] let _profiler = dhat::Profiler::new_heap(); let engine = PlonkEngineConfig::new() diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/tests/serialization.rs similarity index 99% rename from halo2_proofs/examples/serialization.rs rename to halo2_proofs/tests/serialization.rs index 824ddc2a7b..929c61db39 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/tests/serialization.rs @@ -128,7 +128,8 @@ impl Circuit for StandardPlonk { } } -fn main() { +#[test] +fn test_serialization() { let k = 4; let circuit = StandardPlonk(Fr::random(OsRng)); let params = ParamsKZG::::setup(k, OsRng); diff --git a/halo2_proofs/examples/shuffle.rs b/halo2_proofs/tests/shuffle.rs similarity index 99% rename from halo2_proofs/examples/shuffle.rs rename to halo2_proofs/tests/shuffle.rs index f61d17fd09..7ecfb49edc 100644 --- a/halo2_proofs/examples/shuffle.rs +++ b/halo2_proofs/tests/shuffle.rs @@ -314,7 +314,8 @@ fn test_prover( assert_eq!(accepted, expected); } -fn main() { +#[test] +fn test_shuffle() { const W: usize = 4; const H: usize = 32; const K: u32 = 8; diff --git a/halo2_proofs/examples/shuffle_api.rs b/halo2_proofs/tests/shuffle_api.rs similarity index 99% rename from halo2_proofs/examples/shuffle_api.rs rename to halo2_proofs/tests/shuffle_api.rs index f3080cd393..e7034e6f36 100644 --- a/halo2_proofs/examples/shuffle_api.rs +++ b/halo2_proofs/tests/shuffle_api.rs @@ -190,7 +190,8 @@ where assert_eq!(accepted, expected); } -fn main() { +#[test] +fn test_shuffle_api() { use halo2_proofs::dev::MockProver; use halo2curves::pasta::Fp; const K: u32 = 4; diff --git a/halo2_proofs/examples/vector-ops-unblinded.rs b/halo2_proofs/tests/vector-ops-unblinded.rs similarity index 99% rename from halo2_proofs/examples/vector-ops-unblinded.rs rename to halo2_proofs/tests/vector-ops-unblinded.rs index 386d6b34a3..01c24fef4d 100644 --- a/halo2_proofs/examples/vector-ops-unblinded.rs +++ b/halo2_proofs/tests/vector-ops-unblinded.rs @@ -515,7 +515,8 @@ where proof } -fn main() { +#[test] +fn test_vector_ops_unbinded() { use halo2curves::pasta::Fp; const N: usize = 10;