1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::iter;

use super::Argument;
use crate::{
    arithmetic::CurveAffine,
    plonk::circuit::{ExpressionBack, QueryBack, VarBack},
    plonk::{ChallengeGamma, ChallengeTheta, ChallengeX, Error, VerifyingKey},
    poly::{commitment::MSM, VerifierQuery},
    transcript::{EncodedChallenge, TranscriptRead},
};
use halo2_middleware::circuit::Any;
use halo2_middleware::ff::Field;
use halo2_middleware::poly::Rotation;

pub(crate) struct Committed<C: CurveAffine> {
    product_commitment: C,
}

pub(crate) struct Evaluated<C: CurveAffine> {
    committed: Committed<C>,
    product_eval: C::Scalar,
    product_next_eval: C::Scalar,
}

pub(in crate::plonk) fn shuffle_read_product_commitment<
    F: Field,
    C: CurveAffine<ScalarExt = F>,
    E: EncodedChallenge<C>,
    T: TranscriptRead<C, E>,
>(
    transcript: &mut T,
) -> Result<Committed<C>, Error> {
    let product_commitment = transcript.read_point()?;

    Ok(Committed { product_commitment })
}

impl<C: CurveAffine> Committed<C> {
    pub(crate) fn evaluate<E: EncodedChallenge<C>, T: TranscriptRead<C, E>>(
        self,
        transcript: &mut T,
    ) -> Result<Evaluated<C>, Error> {
        let product_eval = transcript.read_scalar()?;
        let product_next_eval = transcript.read_scalar()?;

        Ok(Evaluated {
            committed: self,
            product_eval,
            product_next_eval,
        })
    }
}

impl<C: CurveAffine> Evaluated<C> {
    #[allow(clippy::too_many_arguments)]
    pub(in crate::plonk) fn expressions<'a>(
        &'a self,
        l_0: C::Scalar,
        l_last: C::Scalar,
        l_blind: C::Scalar,
        argument: &'a Argument<C::Scalar>,
        theta: ChallengeTheta<C>,
        gamma: ChallengeGamma<C>,
        advice_evals: &[C::Scalar],
        fixed_evals: &[C::Scalar],
        instance_evals: &[C::Scalar],
        challenges: &[C::Scalar],
    ) -> impl Iterator<Item = C::Scalar> + 'a {
        let active_rows = C::Scalar::ONE - (l_last + l_blind);

        let product_expression = || {
            // z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)
            let compress_expressions = |expressions: &[ExpressionBack<C::Scalar>]| {
                expressions
                    .iter()
                    .map(|expression| {
                        expression.evaluate(
                            &|scalar| scalar,
                            &|var| match var {
                                VarBack::Challenge(challenge) => challenges[challenge.index],
                                VarBack::Query(QueryBack { index, column, .. }) => {
                                    match column.column_type {
                                        Any::Fixed => fixed_evals[index],
                                        Any::Advice => advice_evals[index],
                                        Any::Instance => instance_evals[index],
                                    }
                                }
                            },
                            &|a| -a,
                            &|a, b| a + b,
                            &|a, b| a * b,
                        )
                    })
                    .fold(C::Scalar::ZERO, |acc, eval| acc * *theta + eval)
            };
            // z(\omega X) (s(X) + \gamma)
            let left = self.product_next_eval
                * (compress_expressions(&argument.shuffle_expressions) + *gamma);
            // z(X) (a(X) + \gamma)
            let right =
                self.product_eval * (compress_expressions(&argument.input_expressions) + *gamma);

            (left - right) * active_rows
        };

        std::iter::empty()
            .chain(
                // l_0(X) * (1 - z'(X)) = 0
                Some(l_0 * (C::Scalar::ONE - self.product_eval)),
            )
            .chain(
                // l_last(X) * (z(X)^2 - z(X)) = 0
                Some(l_last * (self.product_eval.square() - self.product_eval)),
            )
            .chain(
                // (1 - (l_last(X) + l_blind(X))) * ( z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma))
                Some(product_expression()),
            )
    }

    pub(in crate::plonk) fn queries<'r, M: MSM<C> + 'r>(
        &'r self,
        vk: &'r VerifyingKey<C>,
        x: ChallengeX<C>,
    ) -> impl Iterator<Item = VerifierQuery<'r, C, M>> + Clone {
        let x_next = vk.domain.rotate_omega(*x, Rotation::next());

        iter::empty()
            // Open shuffle product commitment at x
            .chain(Some(VerifierQuery::new_commitment(
                &self.committed.product_commitment,
                *x,
                self.product_eval,
            )))
            // Open shuffle product commitment at \omega x
            .chain(Some(VerifierQuery::new_commitment(
                &self.committed.product_commitment,
                x_next,
                self.product_next_eval,
            )))
    }
}