2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

halo2-baseの話

Last updated at Posted at 2023-12-09

12/3のゼロ知識証明ミートアップで登壇しました。その時の発表資料。

halo2-baseを使うと

ガス代が安くなります!

このトークでは

halo2-baseを使ってサーキットを最適化していきます

zkp、仕組みを掴むだけでは使いこなせない!

理論家がとりあえず無視しているが、実際に業務で使おうとしたときに問題になることがいろいろある

例: Plonkのarithmetization

カラムの数が多いと、コミットしなければならない多項式の数が増える → vkのサイズが増える → 検証コントラクトのサイズが大きくなる。

ガス代が高すぎる

検証しなければならないオープニングプルーフの数が多すぎる

困った

どうにかして、同じことの制約を、少ない数のカラムで表現する → コミットする必要のある多項式の数が減る → 検証しなければならないオープニングプルーフの数が減る → ガス代が安くなる

どうやってカラムの数を減らす?

halo2-baseの仕組み: 足し算するか掛け算するかの切り替えを、セレクターカラムを使わずに実装する。代わりにcopy constraintを使って切り替える。

微妙に違う方法でArithmetizeする

/// # Vertical Gate Strategy:
/// `q_0 * (a + b * c - d) = 0`
/// where
/// * `a = value[0], b = value[1], c = value[2], d = value[3]`
/// * `q = q_enable[0]`
/// * `q` is either 0 or 1 so this is just a simple selector
/// We chose `a + b * c` instead of `a * b + c` to allow "chaining" of gates, i.e., the output of one gate because `a` in the next gate.


/// Constrains and returns `a + b * 1 = out`.
///
/// Defines a vertical gate of form | a | b | 1 | a + b | where (a + b) = out.
/// * `ctx`: [Context] to add the constraints to
/// * `a`: [QuantumCell] value
/// * `b`: [QuantumCell] value to add to 'a`
fn add(
    &self,
    ctx: &mut Context<F>,
    a: impl Into<QuantumCell<F>>,
    b: impl Into<QuantumCell<F>>,
) -> AssignedValue<F> {
    let a = a.into();
    let b = b.into();
    let out_val = *a.value() + b.value();
    ctx.assign_region_last([a, b, Constant(F::ONE), Witness(out_val)], [0])
}


/// Constrains and returns  `0 + a * b = out`.
///
/// Defines a vertical gate of form | 0 | a | b | a * b |, where (a * b) = out.
/// * `ctx`: [Context] to add the constraints to
/// * `a`: [QuantumCell] value
/// * `b`: [QuantumCell] value to multiply 'a' by
fn mul(
    &self,
    ctx: &mut Context<F>,
    a: impl Into<QuantumCell<F>>,
    b: impl Into<QuantumCell<F>>,
) -> AssignedValue<F> {
    let a = a.into();
    let b = b.into();
    let out_val = *a.value() * b.value();
    ctx.assign_region_last([Constant(F::ZERO), a, b, Witness(out_val)], [0])
}

鶴: 63匹
亀: 37匹

Standard Plonk

赤がプライベートな値。青がパブリックな値。線が copy constraint。

either 足し算 or 掛け算 を、横に制約している。足し算フラグを立てるためだけの多項式 $q_L, q_R$ や、掛け算フラグを立てるためだけの多項式 $q_M$ にコミットする必要があり、vkのサイズが増えてしまう

halo2-base

a + b * c - d = 0 を、縦に制約している。フラグを立てるための多項式が要らなくなる。vkのサイズが小さくなる。
掛け算のみを制約したい時には、足す値を0にすることでごまかす。足し算のみを制約したい時は、掛ける値を1にすることでごまかす。

/// Constrains and returns  `a * b + c = out`.
///
/// Defines a vertical gate of form | c | a | b | a * b + c |, where (a * b + c) = out.
/// * `ctx`: [Context] to add the constraints to
/// * `a`: [QuantumCell] value
/// * `b`: [QuantumCell] value to multiply 'a' by
/// * `c`: [QuantumCell] value to add to 'a * b'
fn mul_add(
    &self,
    ctx: &mut Context<F>,
    a: impl Into<QuantumCell<F>>,
    b: impl Into<QuantumCell<F>>,
    c: impl Into<QuantumCell<F>>,
) -> AssignedValue<F> {
    let a = a.into();
    let b = b.into();
    let c = c.into();
    let out_val = *a.value() * b.value() + c.value();
    ctx.assign_region_last([c, a, b, Witness(out_val)], [0])
}

検証コントラクトをデプロイできるようになったね

vkのサイズ: 458bytes!
コントラクトのサイズ: 15.6KB

ガス代が安くなったね

318819gas

プルーフが小さくなったね

プルーフサイズ: 992bytes
Groth16が288bytesなので、まあ3倍ぐらい

プルーフ生成に必要なRAMの量が減ったね

before: 8columns x 6rows = 48cells
after: 5columns x 6rows = 30cells

まとめ

R1CSと違い、Plonkベースのプルーフシステムでは、同じことを制約する方法が複数ある。
columnsが増えると、検証に必要なガス代が増える。
cellsが増えると、プルーフ生成に必要なRAMが増える。
rowsが増えると、プルーフ生成に必要な時間が増える。
要件に応じて、何を求めて最適化するのかを決める。

所信表明

サーキット最適化芸人として生きていこうと思います
Twitter: @chokermaxx

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?