rank-1 constraint system R1CS

0. 引言

R1CS:即rank-1 constraint system,可将其理解为一个方程组。
Prover需要向Verifier证明其知道满足该方程组所有方程式的解,证明过程可转化为Prover知道3组向量 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )以及1组对应于R1CS解的向量 s ⃗ \vec{s} s ,使得 < s ⃗ , a ⃗ > ∗ < s ⃗ , b ⃗ > − < s ⃗ , c ⃗ > = 0 <\vec{s},\vec{a}>*<\vec{s},\vec{b}>-<\vec{s},\vec{c}>=0 <s ,a ><s ,b ><s ,c >=0成立。

其中 < s ⃗ , a ⃗ > = ∑ i = 1 n s i a i <\vec{s},\vec{a}>=\sum_{i=1}^{n}s_ia_i <s ,a >=i=1nsiai为dot product。

1. r1cs举例

Vitalik 博客Quadratic Arithmetic Programs: from Zero to Hero中主要是举例证明知道 x 3 + x + 5 = = 35 x^3+x+5==35 x3+x+5==35的答案 x = 3 x=3 x=3,将该方程式的证明拆分为如下4组线性等式:
s y m _ 1 = x ∗ x sym\_1=x*x sym_1=xx
y = s y m _ 1 ∗ x y=sym\_1*x y=sym_1x
s y m _ 2 = y + x sym\_2=y+x sym_2=y+x
∼ o u t = s y m _ 2 + 5 \sim out=sym\_2+5 out=sym_2+5

左侧的变量组成为 [ s y m _ 1 , y , s y m _ 2 , ∼ o u t ] [sym\_1,y,sym\_2,\sim out] [sym_1,y,sym_2,out],在此基础上增加1和x变量,组成的向量 s ⃗ = [ ∼ o n e , x , s y m _ 1 , y , s y m _ 2 , ∼ o u t ] \vec{s}=[\sim one,x,sym\_1,y,sym\_2,\sim out] s =[one,x,sym_1,y,sym_2,out]。为了跟Vitalik的博客对应,调整顺序为 s ⃗ = [ ∼ o n e , x , ∼ o u t , s y m _ 1 , y , s y m _ 2 ] \vec{s}=[\sim one,x,\sim out,sym\_1,y,sym\_2] s =[one,x,out,sym_1,y,sym_2]

若Prover知道满足上述方程式组的解,则对应的 s ⃗ = [ ∼ o n e , x , ∼ o u t , s y m _ 1 , y , s y m _ 2 ] = [ 1 , 3 , 35 , 9 , 27 , 30 ] \vec{s}=[\sim one,x,\sim out,sym\_1,y,sym\_2]=[1,3,35,9,27,30] s =[one,x,out,sym_1,y,sym_2]=[1,3,35,9,27,30]
1)对应第一个方程式: s y m _ 1 = x ∗ x sym\_1=x*x sym_1=xx,满足 < s ⃗ , a ⃗ > ∗ < s ⃗ , b ⃗ > − < s ⃗ , c ⃗ > = 0 <\vec{s},\vec{a}>*<\vec{s},\vec{b}>-<\vec{s},\vec{c}>=0 <s ,a ><s ,b ><s ,c >=0成立的 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )为:
a ⃗ = [ 0 , 1 , 0 , 0 , 0 , 0 ] \vec{a}=[0,1,0,0,0,0] a =[0,1,0,0,0,0]
b ⃗ = [ 0 , 1 , 0 , 0 , 0 , 0 ] \vec{b}=[0,1,0,0,0,0] b =[0,1,0,0,0,0]
c ⃗ = [ 0 , 0 , 0 , 1 , 0 , 0 ] \vec{c}=[0,0,0,1,0,0] c =[0,0,0,1,0,0]
2)对应第二个方程式: y = s y m _ 1 ∗ x y=sym\_1*x y=sym_1x,对应的 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )为:
a ⃗ = [ 0 , 0 , 0 , 1 , 0 , 0 ] \vec{a}=[0,0,0,1,0,0] a =[0,0,0,1,0,0]
b ⃗ = [ 0 , 1 , 0 , 0 , 0 , 0 ] \vec{b}=[0,1,0,0,0,0] b =[0,1,0,0,0,0]
c ⃗ = [ 0 , 0 , 0 , 0 , 1 , 0 ] \vec{c}=[0,0,0,0,1,0] c =[0,0,0,0,1,0]
3)对应第三个方程式: s y m _ 2 = y + x sym\_2=y+x sym_2=y+x,对应的 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )为:
a ⃗ = [ 0 , 1 , 0 , 0 , 1 , 0 ] \vec{a}=[0,1,0,0,1,0] a =[0,1,0,0,1,0]
b ⃗ = [ 1 , 0 , 0 , 0 , 0 , 0 ] \vec{b}=[1,0,0,0,0,0] b =[1,0,0,0,0,0]
c ⃗ = [ 0 , 0 , 0 , 0 , 0 , 1 ] \vec{c}=[0,0,0,0,0,1] c =[0,0,0,0,0,1]
4)对应第四个方程式: ∼ o u t = s y m _ 2 + 5 \sim out=sym\_2+5 out=sym_2+5,对应的 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )为:
a ⃗ = [ 0 , 1 , 0 , 0 , 1 , 0 ] \vec{a}=[0,1,0,0,1,0] a =[0,1,0,0,1,0]
b ⃗ = [ 1 , 0 , 0 , 0 , 0 , 0 ] \vec{b}=[1,0,0,0,0,0] b =[1,0,0,0,0,0]
c ⃗ = [ 0 , 0 , 0 , 0 , 0 , 1 ] \vec{c}=[0,0,0,0,0,1] c =[0,0,0,0,0,1]

将所有方程式的 ( a ⃗ , b ⃗ , c ⃗ ) (\vec{a},\vec{b},\vec{c}) (a ,b ,c )向量分别组成 ( A , B , C ) (A,B,C) (A,B,C)三个矩阵,对应的为:
A = [ 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 5 0 0 0 0 1 ] A=\begin{bmatrix} 0& 1 & 0 & 0 & 0 & 0\\ 0& 0 & 0 & 1 & 0 & 0\\ 0& 1 & 0 & 0 & 0 & 1\\ 5& 0 & 0 & 0 & 0 & 1 \end{bmatrix} A=000510100000010000000011
B = [ 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ] B=\begin{bmatrix} 0& 1 & 0 & 0 & 0 & 0\\ 0& 1 & 0 & 0 & 0 & 0\\ 1& 0 & 0 & 0 & 0 & 0\\ 1& 0 & 0 & 0 & 0 & 0 \end{bmatrix} B=001111000000000000000000
C = [ 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 0 ] C=\begin{bmatrix} 0& 0 & 0 & 1 & 0 & 0\\ 0& 0 & 0 & 0 & 1 & 0\\ 0& 0 & 0 & 0 & 0 & 1\\ 0& 0 & 1 & 0 & 0 & 0 \end{bmatrix} C=000000000001100001000010

2. Halo中的R1CS

s ⃗ \vec{s} s 中的 ∼ o n e / x / ∼ o u t \sim one/x/\sim out one/x/out对应:

const ONE: Variable; // ~one
base_var // x
result_var // ~out

pub enum Variable {  // 对应的是每个乘法门的两个输入A和B,一个输出C。C=A*B。
    A(usize), 
    B(usize), 
    C(usize), 
}
pub struct LinearCombination(Vec<(Variable, Coeff)>); //线下方程式由系数和参数组成
pub enum Num { //数值分常量数值和变量数值
    Constant(Coeff),
    Allocated(Coeff, AllocatedNum),
}
impl From<(Coeff, Num)> for Num {
    fn from(num: (Coeff, Num)) -> Self {
        match num.1 {
            Num::Constant(coeff) => Num::Constant(num.0 * coeff),
            Num::Allocated(coeff, n) => Num::Allocated(num.0 * coeff, n),
        }
    }
}

pub struct AllocatedNum {
    value: Option,
    var: Variable,
}

pub enum Coeff {
    Zero,
    One,
    NegativeOne,
    Full(F),
}

	pub fn value(&self) -> F {
        match *self {
            Coeff::Zero => F::zero(),
            Coeff::One => F::one(),
            Coeff::NegativeOne => -F::one(),
            Coeff::Full(val) => val,
        }
    }

pub struct Combination {
    value: Option,
    terms: Vec>,
}

Halo中存在两种类型的circuit:

pub trait RecursiveCircuit {
    fn base_payload(&self) -> Vec;

    fn synthesize>(
        &self,
        cs: &mut CS,
        old_payload: &[AllocatedBit],
        new_payload: &[AllocatedBit],
    ) -> Result<(), SynthesisError>;
}

pub trait Circuit {
    fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError>;
}

/// This is a "namespaced" constraint system which borrows a constraint system (pushing
/// a namespace context) and, when dropped, pops out of the namespace context.
pub struct Namespace<'a, FF: Field, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData);

Halo中对Backend trait做了三次实现,分别是:prove(proof())、verify(verify())和devloping circuit(is_satisfisfied())时。

/// This is a backend for the `SynthesisDriver` to relay information about
/// the concrete circuit. One backend might just collect basic information
/// about the circuit for verification, while another actually constructs
/// a witness.
pub trait Backend {
    type LinearConstraintIndex;

    /// Get the value of a variable. Can return None if we don't know.
    fn get_var(&self, _var: Variable) -> Option {
        None
    }

    /// Set the value of a variable.
    ///
    /// `allocation` will be Some if this multiplication gate is being used for
    /// variable allocation, and None if it is being used as a constraint.
    ///
    /// Might error if this backend expects to know it.
    fn set_var(
        &mut self,
        _annotation: Option,
        _var: Variable,
        _value: F,
    ) -> Result<(), SynthesisError>
    where
        F: FnOnce() -> Result,
        A: FnOnce() -> AR,
        AR: Into,
    {
        Ok(())
    }

    /// Create a new multiplication gate.
    ///
    /// `allocation` will be Some if this multiplication gate is being used as a
    /// constraint, and None if it is being used for variable allocation.
    fn new_multiplication_gate(&mut self, _annotation: Option)
    where
        A: FnOnce() -> AR,
        AR: Into,
    {
    }

    /// Create a new linear constraint, returning a cached index.
    fn new_linear_constraint(&mut self, annotation: A) -> Self::LinearConstraintIndex
    where
        A: FnOnce() -> AR,
        AR: Into;

    /// Insert a term into a linear constraint.
    fn insert_coefficient(
        &mut self,
        _var: Variable,
        _coeff: Coeff,
        _y: &Self::LinearConstraintIndex,
    ) {
    }

    /// Compute a `LinearConstraintIndex` from `q`.
    fn get_for_q(&self, q: usize) -> Self::LinearConstraintIndex;

    /// Mark y^{_index} as the power of y cooresponding to the public input
    /// coefficient for the next public input, in the k(Y) polynomial. Also
    /// gives the value of the public input.
    fn new_k_power(&mut self, _index: usize, _value: Option) -> Result<(), SynthesisError> {
        Ok(())
    }

    /// Create a new (sub)namespace and enter into it.
    fn push_namespace(&mut self, _name_fn: N)
    where
        NR: Into,
        N: FnOnce() -> NR,
    {
    }

    /// Exit out of the existing namespace.
    fn pop_namespace(&mut self, _gadget_name: Option) {}
}
struct Synthesizer> {
            backend: B,
            current_variable: Option,
            _marker: PhantomData,
            //对应的即为论文《Efficient Zero-Knowledge Arguments for Arithmetic Circuits in the Discrete Log Setting》P24页中的Q linear constraints on the wires......
            q: usize, //q为Linear constraints的数量,k(Y)=\sum_{q=1}^{Q}k_q*Y^q。
            n: usize, //n为乘法门的序号。每个乘法门有两个输入,分别是a和b,有一个输出c。c=a*b。
        }

2.1 xor异或操作的R1CS

异或操作a^b=c,对应的R1CS表示为:(a + a) * (b) = (a + b - c),详细的推理过程如下:

		// ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c
        // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c
        // (1 - ab) * (1 - (1 - a - b + ab)) = c
        // (1 - ab) * (a + b - ab) = c
        // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c
        // a + b - ab - ab - ab + ab = c
        // a + b - 2ab = c
        // -2a * b = c - a - b
        // 2a * b = a + b - c
        //
        // d * e = f
        // d = 2a
        // e = b
        // f = a + b - c

ConstraintSystem由大量LinearCombination组成。

/// Constrain (x)^5 = (x^5), and return variables for x and (x^5).
///
/// We can do so with three multiplication constraints and five linear constraints:
///
/// a * b = c
/// a := x
/// b = a
/// c := x^2
///
/// d * e = f
/// d = c
/// e = c
/// f := x^4
///
/// g * h = i
/// g = f
/// h = x
/// i := x^5
fn constrain_pow_five(
    mut cs: CS,
    x: Option,
) -> Result<(Variable, Variable), SynthesisError>
where
    F: Field,
    CS: ConstraintSystem,
{
    let x2 = x.and_then(|x| Some(x.square()));
    let x4 = x2.and_then(|x2| Some(x2.square()));
    let x5 = x4.and_then(|x4| x.and_then(|x| Some(x4 * x)));

    let (base_var, b_var, c_var) = cs.multiply(
        || "x^2",
        || {
            let x = x.ok_or(SynthesisError::AssignmentMissing)?;
            let x2 = x2.ok_or(SynthesisError::AssignmentMissing)?;

            Ok((x, x, x2))
        },
    )?;
    cs.enforce_zero(LinearCombination::from(base_var) - b_var);

    let (d_var, e_var, f_var) = cs.multiply(
        || "x^4",
        || {
            let x2 = x2.ok_or(SynthesisError::AssignmentMissing)?;
            let x4 = x4.ok_or(SynthesisError::AssignmentMissing)?;

            Ok((x2, x2, x4))
        },
    )?;
    cs.enforce_zero(LinearCombination::from(c_var) - d_var);
    cs.enforce_zero(LinearCombination::from(c_var) - e_var);

    let (g_var, h_var, result_var) = cs.multiply(
        || "x^5",
        || {
            let x = x.ok_or(SynthesisError::AssignmentMissing)?;
            let x4 = x4.ok_or(SynthesisError::AssignmentMissing)?;
            let x5 = x5.ok_or(SynthesisError::AssignmentMissing)?;

            Ok((x4, x, x5))
        },
    )?;
    cs.enforce_zero(LinearCombination::from(f_var) - g_var);
    cs.enforce_zero(LinearCombination::from(base_var) - h_var);

    Ok((base_var, result_var))
}

参考资料:
[1] Vitalik 博客Quadratic Arithmetic Programs: from Zero to Hero

你可能感兴趣的:(rank-1 constraint system R1CS)