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 ⃗ = [ 5 , 0 , 0 , 0 , 0 , 1 ] \vec{a}=[5,0,0,0,0,1] a =[5,0,0,0,0,1]
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 , 1 , 0 , 0 , 0 ] \vec{c}=[0,0,1,0,0,0] c =[0,0,1,0,0,0]

将所有方程式的 ( 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<F: Field>(Vec<(Variable, Coeff<F>)>); //线下方程式由系数和参数组成
pub enum Num<F: Field> { //数值分常量数值和变量数值
    Constant(Coeff<F>),
    Allocated(Coeff<F>, AllocatedNum<F>),
}
impl<F: Field> From<(Coeff<F>, Num<F>)> for Num<F> {
    fn from(num: (Coeff<F>, Num<F>)) -> 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<F: Field> {
    value: Option<F>,
    var: Variable,
}

pub enum Coeff<F: Field> {
    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<F: Field> {
    value: Option<F>,
    terms: Vec<Num<F>>,
}

Halo中存在两种类型的circuit:

pub trait RecursiveCircuit<F: Field> {
    fn base_payload(&self) -> Vec<bool>;

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

pub trait Circuit<F: Field> {
    fn synthesize<CS: ConstraintSystem<F>>(&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<FF> + 'a>(&'a mut CS, PhantomData<FF>);

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<FF: Field> {
    type LinearConstraintIndex;

    /// Get the value of a variable. Can return None if we don't know.
    fn get_var(&self, _var: Variable) -> Option<FF> {
        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<F, A, AR>(
        &mut self,
        _annotation: Option<A>,
        _var: Variable,
        _value: F,
    ) -> Result<(), SynthesisError>
    where
        F: FnOnce() -> Result<FF, SynthesisError>,
        A: FnOnce() -> AR,
        AR: Into<String>,
    {
        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<A, AR>(&mut self, _annotation: Option<A>)
    where
        A: FnOnce() -> AR,
        AR: Into<String>,
    {
    }

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

    /// Insert a term into a linear constraint.
    fn insert_coefficient(
        &mut self,
        _var: Variable,
        _coeff: Coeff<FF>,
        _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<FF>) -> Result<(), SynthesisError> {
        Ok(())
    }

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

    /// Exit out of the existing namespace.
    fn pop_namespace(&mut self, _gadget_name: Option<String>) {}
}
struct Synthesizer<F: Field, B: Backend<F>> {
            backend: B,
            current_variable: Option<usize>,
            _marker: PhantomData<F>,
            //对应的即为论文《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<F, CS>(
    mut cs: CS,
    x: Option<F>,
) -> Result<(Variable, Variable), SynthesisError>
where
    F: Field,
    CS: ConstraintSystem<F>,
{
    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

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值