Polygon PIL-STARK 源碼分析

本文以Plookup約束為例對(duì)PIL-STARK源碼進(jìn)行分析六孵。

1. 定義starkStruct

首先定義starkStruct 結(jié)構(gòu)為:

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

2. 編譯PIL

Plookup 的PIL 代碼有兩個(gè)文件:

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

對(duì)PIL 源碼進(jìn)行編譯:

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

得到PIL為:

{
 "nCommitments": 1, //承諾多項(xiàng)式的個(gè)數(shù)
 "nQ": 0,
 "nIm": 0,
 "nConstants": 4, //常量承諾多項(xiàng)式的個(gè)數(shù)
 "publics": [],
 "references": {
  "Global.L1": {
   "type": "constP",  //常量承諾多項(xiàng)式類(lèi)型
   "id": 0,        //在`constP` 的id
   "polDeg": 8,       
   "isArray": false
  },
  "SimplePlookup.a": {
   "type": "cmP",      //承諾多項(xiàng)式類(lèi)型
   "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": [  // 包含兩個(gè)表達(dá)式
  {
   "op": "cm",
   "deg": 1,
   "id": 0,
   "next": false
  },
  {
   "op": "const",
   "deg": 1,
   "id": 1,
   "next": false
  }
 ],
 "polIdentities": [],
 "plookupIdentities": [
  {
   "f": [
    0         // 表示式 id
   ],
   "t": [
    1          // 表達(dá)式 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 的結(jié)果
        varPolMap: [],
        puCtx: [],
        peCtx: [],
        ciCtx: []
    };

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

    generatePublicCalculators(res, pil);  // 處理公開(kāi)輸入
    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(); //用于表達(dá)式的運(yùn)算

    for (let i=0; i<pil.plookupIdentities.length; i++) {  //循環(huán)結(jié)構(gòu)拷泽, 對(duì)PIL的每個(gè)plookup趴俘, 分別處理
        const puCtx = {};
        const pi = pil.plookupIdentities[i];
        
        // 計(jì)算 t 表達(dá)式  
        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);

        
        // 計(jì)算 f  表達(dá)式
        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++;   // 每個(gè)plookup 新添加h1和h2 兩個(gè)承諾:
        puCtx.h2Id = pil.nCommitments++;

        res.puCtx.push(puCtx);
    }

    res.step2prev = buildCode(ctx);
}

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

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 個(gè)表達(dá)式
3:{op: 'exp', id: 0, next: false, keep: true}  // fExp: id 0 指向第 0 個(gè)表達(dá)式

對(duì) fExp 執(zhí)行 pilCodeGen 操作后硼一,

 pilCodeGen(ctx, puCtx.fExpId, false);

ctx 添加2兩個(gè)code:

// 第0個(gè)code, 執(zhí)行復(fù)制操作 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 個(gè) code, 執(zhí)行復(fù)制操作 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}

對(duì)tExp 執(zhí)行 pilCodeGen 運(yùn)算:

pilCodeGen(ctx, puCtx.tExpId, false);

ctx 再次添加兩個(gè)code, 分別:

// 第2個(gè)code, 執(zhí)行復(fù)制操作 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個(gè)code, 執(zhí)行復(fù)制操作 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的 結(jié)構(gòu)為:

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

buildCode 整合所有的code浸卦, 共4個(gè)程帕,分別放于first, i, last 中:

res.step2prev = buildCode(ctx);

和上述的code 順序?qū)?yīng):

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步的承諾個(gè)數(shù)该默。

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

6. generateStep3

generatePermutationsLC

對(duì)置換進(jìn)行處理瞳氓,本例不需要,略過(guò)权均;

generatePlookupZ

對(duì)查找表進(jìn)行處理顿膨。

下面的計(jì)算對(duì)應(yīng)著Plooup 算法:

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

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


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

        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)));  // 對(duì)應(yīng) z(g) = 1 約束條件
        c1.deg=2;
        puCtx.c1Id = pil.expressions.length; // 表達(dá)式的 id
        pil.expressions.push(c1);  
        pil.polIdentities.push({e: puCtx.c1Id}); //  最終表達(dá)式放于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;  // 分子表達(dá)式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; // 分母表達(dá)式 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)  ); // 對(duì)就上文公式 4(b)表達(dá)
        c2.deg=2;
        puCtx.c2Id = pil.expressions.length;   // c2 表達(dá)式 id 
        pil.expressions.push(c2);
        pil.polIdentities.push({e: puCtx.c2Id});    // 表達(dá)式約束放入poldIdentities中叽赊。

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

得到的puCtx 結(jié)構(gòu)為:

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

generatePermutationZ

生成置換多項(xiàng)式Z, 本例不需要必搞,略過(guò)

generateConnectionZ

生成ConnectinsZ必指, 本例不需要,略過(guò)

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

    res.step3prev = buildCode(ctx);

計(jì)算第三步的承諾個(gè)數(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++) {  // 將所有的約束表達(dá)式線性組合在一起
        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;  // 組合表達(dá)式的 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恕洲,一起剝皮案震驚了整個(gè)濱河市塔橡,隨后出現(xiàn)的幾起案子梅割,更是在濱河造成了極大的恐慌,老刑警劉巖葛家,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户辞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡癞谒,警方通過(guò)查閱死者的電腦和手機(jī)底燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)弹砚,“玉大人双仍,你說(shuō)我怎么就攤上這事∽莱裕” “怎么了朱沃?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茅诱。 經(jīng)常有香客問(wèn)我逗物,道長(zhǎng),這世上最難降的妖魔是什么瑟俭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任敬察,我火速辦了婚禮,結(jié)果婚禮上尔当,老公的妹妹穿的比我還像新娘莲祸。我一直安慰自己,他們只是感情好椭迎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布锐帜。 她就那樣靜靜地躺著,像睡著了一般畜号。 火紅的嫁衣襯著肌膚如雪缴阎。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天简软,我揣著相機(jī)與錄音蛮拔,去河邊找鬼。 笑死痹升,一個(gè)胖子當(dāng)著我的面吹牛建炫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疼蛾,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肛跌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起衍慎,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤转唉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后稳捆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赠法,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年乔夯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砖织。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驯嘱,死狀恐怖镶苞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鞠评,我是刑警寧澤茂蚓,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站剃幌,受9級(jí)特大地震影響聋涨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜负乡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一牍白、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抖棘,春花似錦茂腥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至朝捆,卻和暖如春般渡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芙盘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工驯用, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人儒老。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓蝴乔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贷盲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淘这,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容