Polygon PIL-STARK 源碼分析

本文以Plookup約束為例對PIL-STARK源碼進行分析。

1. 定義starkStruct

首先定義starkStruct 結構為:

        const starkStruct = {
            nBits: 3,   // 基域 2**3 = 8
            nBitsExt: 4,  // 擴域:2**4 = 6 
            nQueries: 8,
            verificationHashType: "GL",  // 采用GoldLicks Hash
            steps: [
                { nBits: 4 },
                { nBits: 2 }
            ]
        };

2. 編譯PIL

Plookup 的PIL 代碼有兩個文件:

simple_plookup_main.pil為:

constant %N = 2**3;

namespace Global(%N);
    pol constant L1;  // Lagrange basis 

include "simple_plookup.pil";

simple_plookup.pil 為:

namespace SimplePlookup(%N);


    pol commit a;  // commitment polynomia

    pol constant A;   // constant polynomial
    pol constant ONE;
    pol constant ZERO;

    a in A;   // plookup

對PIL 源碼進行編譯:

const pil = await compile(Fr, path.join(__dirname, "sm_simple_plookup", "simple_plookup_main.pil"));

得到PIL為:

{
 "nCommitments": 1, //承諾多項式的個數(shù)
 "nQ": 0,
 "nIm": 0,
 "nConstants": 4, //常量承諾多項式的個數(shù)
 "publics": [],
 "references": {
  "Global.L1": {
   "type": "constP",  //常量承諾多項式類型
   "id": 0,        //在`constP` 的id
   "polDeg": 8,       
   "isArray": false
  },
  "SimplePlookup.a": {
   "type": "cmP",      //承諾多項式類型
   "id": 0,           //在`cmP` 承諾中的id
   "polDeg": 8,
   "isArray": false
  },
  "SimplePlookup.A": {
   "type": "constP",
   "id": 1,
   "polDeg": 8,
   "isArray": false
  },
  "SimplePlookup.ONE": {
   "type": "constP",
   "id": 2,
   "polDeg": 8,
   "isArray": false
  },
  "SimplePlookup.ZERO": {
   "type": "constP",
   "id": 3,
   "polDeg": 8,
   "isArray": false
  }
 },
 "expressions": [  // 包含兩個表達式
  {
   "op": "cm",
   "deg": 1,
   "id": 0,
   "next": false
  },
  {
   "op": "const",
   "deg": 1,
   "id": 1,
   "next": false
  }
 ],
 "polIdentities": [],
 "plookupIdentities": [
  {
   "f": [
    0         // 表示式 id
   ],
   "t": [
    1          // 表達式 id
   ],
   "selF": null,
   "selT": null,
   "fileName": "simple_plookup.pil",
   "line": 10
  }
 ],
 "permutationIdentities": [],
 "connectionIdentities": []
}

3. build constPols and cmPols

其中 N=8.

module.exports.buildConstants = async function (pols) {
    const N = pols.A.length;

    let p = 0;
    for (let i = 0; i < N; i++) {
        pols.A[i] = BigInt(i);
        pols.ONE[i] = 1n;
        pols.ZERO[i] = 0n;
    }
}

module.exports.execute = async function (pols) {

    const N = pols.a.length;

    for (let i = 0; i < N; i++) {
        pols.a[i] = BigInt(i);
    }
}

4. StarkInfo 初始化

    const res = {     // 用于保存最終的StarkInfo 的結果
        varPolMap: [],
        puCtx: [],
        peCtx: [],
        ciCtx: []
    };

    res.starkStruct = starkStruct;
    res.nConstants = pil.nConstants;
    res.nPublics = pil.publics.length;

    generatePublicCalculators(res, pil);  // 處理公開輸入
    res.nCm1 = pil.nCommitments;

    const ctx = {
        pil: pil,
        calculated: {
            exps: {},
            expsPrime: {}
        },
        tmpUsed: 0,
        code: []
    };

    const ctx2ns = {
        pil: pil,
        calculated: {
            exps: {},
            expsPrime: {}
        },
        tmpUsed: 0,
        code: []
    };

5. generateStep2

主要的代碼如下:

module.exports = function generateStep2(res, pil, ctx) {

    const E = new ExpressionOps(); //用于表達式的運算

    for (let i=0; i<pil.plookupIdentities.length; i++) {  //循環(huán)結構, 對PIL的每個plookup, 分別處理
        const puCtx = {};
        const pi = pil.plookupIdentities[i];
        
        // 計算 t 表達式  
        let tExp = null;
        const u = E.challenge("u");
        const defVal = E.challenge("defVal");
        for (let j=0; j<pi.t.length; j++) {
            const e = E.exp(pi.t[j]);
            if (tExp) {
                tExp = E.add(E.mul(u, tExp), e);
            } else {
                tExp = e;
            }
        }
        if (pi.selT !== null) {
            tExp = E.sub(tExp, defVal);
            tExp = E.mul(tExp, E.exp(pi.selT));
            tExp = E.add(tExp, defVal);

            tExp.idQ = pil.nQ;  //Q 可能表示 Quadraic, 二次的
            pil.nQ++;    
        }

        puCtx.tExpId = pil.expressions.length;
        tExp.keep = true;
        pil.expressions.push(tExp);

        
        // 計算 f  表達式
        fExp = null;
        for (let j=0; j<pi.f.length; j++) {
            const e = E.exp(pi.f[j]);
            if (fExp) {
                fExp = E.add(E.mul(fExp, u), e);
            } else {
                fExp = e;
            }
        }
        if (pi.selF !== null) {
            fExp = E.sub(fExp, E.exp(puCtx.tExpId));
            fExp = E.mul(fExp, E.exp(pi.selF));
            fExp = E.add(fExp, E.exp(puCtx.tExpId));

            fExp.idQ = pil.nQ;   
            pil.nQ++;
        }

        puCtx.fExpId = pil.expressions.length;
        fExp.keep = true;
        pil.expressions.push(fExp);

        pilCodeGen(ctx, puCtx.fExpId, false);
        pilCodeGen(ctx, puCtx.tExpId, false);

        puCtx.h1Id = pil.nCommitments++;   // 每個plookup 新添加h1和h2 兩個承諾:
        puCtx.h2Id = pil.nCommitments++;

        res.puCtx.push(puCtx);
    }

    res.step2prev = buildCode(ctx);
}

pil.expressions 數(shù)組目前變?yōu)?個表達式:

0:{op: 'cm', deg: 1, id: 0, next: false}
1:{op: 'const', deg: 1, id: 1, next: false}
2:{op: 'exp', id: 1, next: false, keep: true} // tExp: id 1 指向第 1 個表達式
3:{op: 'exp', id: 0, next: false, keep: true}  // fExp: id 0 指向第 0 個表達式

fExp 執(zhí)行 pilCodeGen 操作后,

 pilCodeGen(ctx, puCtx.fExpId, false);

ctx 添加2兩個code:

// 第0個code, 執(zhí)行復制操作 cm0 -> exp 0 
0:{expId: 0, prime: false, code: Array(1), idQ: undefined}
code:(1) [{…}]
0:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 0}
op:'copy'
src:(1) [{…}]
0:{type: 'cm', id: 0, prime: false}

// 第 1 個 code, 執(zhí)行復制操作 exp 0 -> exp3
1:{expId: 3, prime: false, code: Array(1), idQ: undefined}
code:(1) [{…}]
0:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 3}
id:3
prime:false
type:'exp'
op:'copy'
src:(1) [{…}]
0:{type: 'exp', id: 0, prime: false}

對tExp 執(zhí)行 pilCodeGen 運算:

pilCodeGen(ctx, puCtx.tExpId, false);

ctx 再次添加兩個code, 分別:

// 第2個code, 執(zhí)行復制操作 const 1 -> exp 1 
2:{expId: 1, prime: false, code: Array(1), idQ: undefined}
code:(1) [{…}]
0:{op: 'copy', dest: {…}, src: Array(1)}
length:1
expId:1
dest:{type: 'exp', prime: false, id: 1}
op:'copy'
src:(1) [{…}]
0:{type: 'const', id: 1, prime: false}

// 第3個code, 執(zhí)行復制操作 exp 1 -> exp 2
3:{expId: 2, prime: false, code: Array(1), idQ: undefined}
code:(1) [{…}]
0:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 2}
op:'copy'
src:(1) [{…}]
0:{type: 'exp', id: 1, prime: false}

生成puCtx的 結構為:

{tExpId: 2, fExpId: 3, h1Id: 1, h2Id: 2}
fExpId:3 // f 表達式 id
h1Id:1   // h1 承諾(cm) id 
h2Id:2     // h2 承諾 id 
tExpId:2   // t 表達式 id 

buildCode 整合所有的code, 共4個,分別放于first, i, last 中:

res.step2prev = buildCode(ctx);

和上述的code 順序對應:

first:(4) [{…}, {…}, {…}, {…}]
0:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 0}
op:'copy'
src:(1) [{…}]
0:{type: 'cm', id: 0, prime: false}

1:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 3}
op:'copy'
src:(1) [{…}]
0:{type: 'exp', id: 0, prime: false}


2:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 1}
op:'copy'
src:(1) [{…}]
0:{type: 'const', id: 1, prime: false}


3:{op: 'copy', dest: {…}, src: Array(1)}
dest:{type: 'exp', prime: false, id: 2}
op:'copy'
src:(1) [{…}]
0:{type: 'exp', id: 1, prime: false}

nCm2 表示為第2步的承諾個數(shù)。

res.nCm2 = pil.nCommitments - res.nCm1;

6. generateStep3

generatePermutationsLC

對置換進行處理,本例不需要,略過;

generatePlookupZ

對查找表進行處理。

下面的計算對應著Plooup 算法:

function generatePlookupZ(res, pil, ctx) {
    const E = new ExpressionOps();

    for (let i=0; i<pil.plookupIdentities.length; i++) { // 循環(huán),對每個plookup操作分別處理
        const puCtx = res.puCtx[i];   
        puCtx.zId = pil.nCommitments++;  // 添加一個承諾


        const h1 = E.cm(puCtx.h1Id);     // 生成承諾表達式
        const h2 =  E.cm(puCtx.h2Id);        // 生成承諾表達式
        const h1p = E.cm(puCtx.h1Id, true);   //h1的下一步
        const f = E.exp(puCtx.fExpId);       // f 表達式
        const t = E.exp(puCtx.tExpId);       // t 表達式
        const tp = E.exp(puCtx.tExpId, true);   // t 表達式下一步
        const z = E.cm(puCtx.zId);             // z 表達式
        const zp = E.cm(puCtx.zId, true);     // z 表達式下一步

        if ( typeof pil.references["Global.L1"] === "undefined") throw new Error("Global.L1 must be defined");

        const l1 = E.const(pil.references["Global.L1"].id);

        const c1 = E.mul(l1,  E.sub(z, E.number(1)));  // 對應 z(g) = 1 約束條件
        c1.deg=2;
        puCtx.c1Id = pil.expressions.length; // 表達式的 id
        pil.expressions.push(c1);  
        pil.polIdentities.push({e: puCtx.c1Id}); //  最終表達式放于polIdentities 中
 
        const gamma = E.challenge("gamma");
        const beta = E.challenge("beta");

        const numExp = E.mul(
            E.mul(
                E.add(f, gamma),
                E.add(
                    E.add(
                        t,
                        E.mul(
                            tp,
                            beta
                        )
                    ),
                    E.mul(gamma,E.add(E.number(1), beta))
                )
            ),
            E.add(E.number(1), beta)
        );
        numExp.idQ = pil.nQ++;
        puCtx.numId = pil.expressions.length;  // 分子表達式id 
        numExp.keep = true;
        pil.expressions.push(numExp);       

        const denExp = E.mul(
            E.add(
                E.add(
                    h1,
                    E.mul(
                        h2,
                        beta
                    )
                ),
                E.mul(gamma,E.add(E.number(1), beta))
            ),
            E.add(
                E.add(
                    h2,
                    E.mul(
                        h1p,
                        beta
                    )
                ),
                E.mul(gamma,E.add(E.number(1), beta))
            )
        );
        denExp.idQ = pil.nQ++;
        puCtx.denId = pil.expressions.length; // 分母表達式 id 
        denExp.keep = true;
        pil.expressions.push(denExp);

        const num = E.exp(puCtx.numId);
        const den = E.exp(puCtx.denId);

        const c2 = E.sub(  E.mul(zp, den), E.mul(z, num)  ); // 對就上文公式 4(b)表達
        c2.deg=2;
        puCtx.c2Id = pil.expressions.length;   // c2 表達式 id 
        pil.expressions.push(c2);
        pil.polIdentities.push({e: puCtx.c2Id});    // 表達式約束放入poldIdentities中。

        pilCodeGen(ctx, puCtx.numId, false); // 生成分子表達式的code
        pilCodeGen(ctx, puCtx.denId, false);  // 生成分母表達式的code
    }
}

得到的puCtx 結構為:

  {
   "tExpId": 2,   // t 表達式id
   "fExpId": 3,   // f 表達式id 
   "h1Id": 1,     // h1 承諾id 
   "h2Id": 2,     // h2 承諾id 
   "zId": 3,      // z 承諾id 
   "c1Id": 4,     // c1 表達式id
   "numId": 5,     // num 表達式id
   "denId": 6,    // den 表達式id
   "c2Id": 7      // c2 表達式 id 
  }

generatePermutationZ

生成置換多項式Z, 本例不需要,略過

generateConnectionZ

生成ConnectinsZ, 本例不需要,略過

最后生成第code, 如下所示:

    res.step3prev = buildCode(ctx);

計算第三步的承諾個數(shù):

    res.nCm3 = pil.nCommitments - res.nCm1 - res.nCm2;

7. generateConstraintPolynomial

module.exports = function generateConstraintPolynomial(res, pil, ctx, ctx2ns) {

    const E = new ExpressionOps();

    const vc = E.challenge("vc");
    let cExp = null;
    for (let i=0; i<pil.polIdentities.length; i++) {  // 將所有的約束表達式線性組合在一起
        const e = E.exp(pil.polIdentities[i].e);
        if (cExp) {
            cExp = E.add(E.mul(vc, cExp), e);
        } else {
            cExp = e;
        }
    }
    cExp.idQ = pil.nQ++;
    res.cExp = pil.expressions.length;  // 組合表達式的 id
    pil.expressions.push(cExp);

    for (let i=0; i<pil.expressions.length; i++) {
        if (typeof pil.expressions[i].idQ != "undefined") {
            pilCodeGen(ctx, i);                            
            pilCodeGen(ctx2ns, i, false, "evalQ");      //
        }
    }

    res.step4 = buildCode(ctx);
    res.step42ns = buildCode(ctx2ns);
}

參考

https://github.com/0xPolygonHermez/pil-stark/blob/main/test/stark_simple_plookup.test.js

https://eprint.iacr.org/2020/315.pdf

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容