Polygon zkEVM 递归证明过程

2023-09-11  本文已影响0人  雪落无留痕

2022年12月21日,Polygon zkEVM 发布了第二个测试网,也是主网上线前最后一个测试网。此次升级主要在zkEVM 实现递归证明,实现Batch 聚合过程,并将每个Batch证明时间降低从原先的10min 降到 4min 以下,每个Batch的Gas limit 从 4 millon 增加到 10 milion, 进一步降低交易费用,增加吞吐量,并且还引入了EIP-155 (引入chain_id, 防止重入攻击) 的支持。

recursive1

生成recursive1证明需要两轮 stark->stark 证明,即 \pi_{batch}\rightarrow \pi_{c12a} \rightarrow \pi_{segment}, 皆定义在Goldilocks 域上。

对于STARK 转换为STARK, 首先通过PIL2Circom 过程,生成circom 验证电路,用于验证STARK证明; 再将circom 电路编译成R1CS 之后,转换变Plonk 形式的电路,再生成PIL 的描述,通过这个过程,可以用于验证STARK 证明的PIL约束; 然后根据PIL, 可以生成新的STARK证明.

因此recursive1 证明的实现过程如下:

关于具体的解析过程,详见 STARK->SNARK 解析文档。

生成\pi_{batch} 证时的Stark struct 结构为:

{
            "nBits": 23,  // 基域
            "nBitsExt": 24,  // 扩域
            "nQueries": 128,  // FRI 查询点的个数
            "verificationHashType": "GL",  // Goldilocks 域
            "steps": [
                {"nBits": 24},   // FRI各层域的位数 
                {"nBits": 19},
                {"nBits": 14},
                {"nBits": 10},
                {"nBits": 6}
            ]
}

生成 \pi_{C12a} 证明时的Stark struct 结构为:

{
    "nBits": 22,
    "nBitsExt": 24,
    "nQueries": 64,
    "verificationHashType": "GL",
    "steps": [
        {"nBits": 24},
        {"nBits": 20},
        {"nBits": 15},
        {"nBits": 10},
        {"nBits": 5}
    ]
}

recursive1 验证\pi_b 的验证电路recursive1.circom为:

pragma circom 2.1.0;
pragma custom_templates;

include "c12a.verifier.circom";

template Main() {
    
    //publics 43个元素分别为:
    // 0-7: oldStateRoot; 8-15: OldAccInputHash; 16: oldBatchNum; 17: chainId, 
    // 18-25:newStateRoot; 26-33: newAccInputHash; 34-41: localExitRoot, 42: newBatchNum 
    signal input publics[43];         
    signal input rootC[4]; // 常量多项式对应的Merkle 根

    signal input root1[4]; // commit多项式对应的Merkle 根   
    signal input root2[4];  // plookup 多项式对应的Merkle 根
    signal input root3[4];  // plookup/permutation/connection Z 多项式对应的根
    signal input root4[4];  // q多项式 约束对应的Merkle 根
    signal input evals[94][3]; // 对应starkinfo 中 evMap中各个承诺多项式的值。

    signal input s0_vals1[64][12]; //FRI proof[0]: 64 表示查询点的个数,12表示commit多项式的个数(cm1_2ns)
    signal input s0_vals3[64][41]; 
    //FRI proof[0]: 64 表示查询点的个数,41表示plookup/permutation/connection Z多项式的个数
    
    signal input s0_vals4[64][12]; //FRI proof[0]: 64 表示查询点的个数,12表示 q多项式的个数(q_2ns)
    signal input s0_valsC[64][34]; //FRI proof[0]: 64 表示查询点的个数,34表示常量多项式的个数
    signal input s0_siblings1[64][24][4]; //s0_vals1对应的Merkle 路径,有24层,每个节点由4个Goldilocks 元素组成
    signal input s0_siblings3[64][24][4];
    signal input s0_siblings4[64][24][4];
    signal input s0_siblingsC[64][24][4];

    signal input s1_root[4]; // fri step 1 root
    signal input s2_root[4]; // fri step 2 root
    signal input s3_root[4]; // fri step 3 root
    signal input s4_root[4]; // fri step 4 root

    signal input s1_vals[64][48]; // fri 1 tree对应查询点的叶子节点,48为对应叶子width  
    signal input s1_siblings[64][20][4]; // fri 1 tree 查询点对应的Merkle路径
    signal input s2_vals[64][96];
    signal input s2_siblings[64][15][4];
    signal input s3_vals[64][96];
    signal input s3_siblings[64][10][4];
    signal input s4_vals[64][96];
    signal input s4_siblings[64][5][4];

    signal input finalPol[32][3]; // 最后一轮,每个都为宽度为3的值



    component vA = StarkVerifier();

    vA.publics <== publics;
    vA.root1 <== root1;
    vA.root2 <== root2;
    vA.root3 <== root3;
    vA.root4 <== root4;
    vA.evals <== evals;
    vA.s0_vals1 <== s0_vals1;
    vA.s0_vals3 <== s0_vals3;
    vA.s0_vals4 <== s0_vals4;
    vA.s0_valsC <== s0_valsC;
    vA.s0_siblings1 <== s0_siblings1;
    vA.s0_siblings3 <== s0_siblings3;
    vA.s0_siblings4 <== s0_siblings4;
    vA.s0_siblingsC <== s0_siblingsC;
    vA.s1_root <== s1_root;
    vA.s2_root <== s2_root;
    vA.s3_root <== s3_root;
    vA.s4_root <== s4_root;
    vA.s1_vals <== s1_vals;
    vA.s1_siblings <== s1_siblings;
    vA.s2_vals <== s2_vals;
    vA.s2_siblings <== s2_siblings;
    vA.s3_vals <== s3_vals;
    vA.s3_siblings <== s3_siblings;
    vA.s4_vals <== s4_vals;
    vA.s4_siblings <== s4_siblings;
    vA.finalPol <== finalPol;

}

component main {public [publics, rootC]}= Main(); // 公开输入为:publics 和 常量多项式根

生成recursive1 证明时的的 Stark struct 为:

{
    "nBits": 20,
    "nBitsExt": 24,
    "nQueries": 32,
    "verificationHashType": "GL",
    "steps": [
        {"nBits": 24},
        {"nBits": 19},
        {"nBits": 14},
        {"nBits": 9},
        {"nBits": 5}
    ]
}

recursive2

通过合并两个recursive1证明的验证过程,生成recursive2 证明。

recrsive2的验证电路recursive2.circom.ejs为, 其中包含了两个recursive1 证明的验证。

注: 在进行ejs注入时,需要提供recursive1 常量根。

pragma circom 2.1.0;
pragma custom_templates;

include "recursive1.verifier.circom";
include "mux1.circom";
include "iszero.circom";

template Main() {

    var rootCSingle[4];
    rootCSingle[0] = <%- constRoot[0] %>;
    rootCSingle[1] = <%- constRoot[1] %>;
    rootCSingle[2] = <%- constRoot[2] %>;
    rootCSingle[3] = <%- constRoot[3] %>;

    signal input publics[43];
    signal input rootC[4];

    signal input a_publics[43];

    signal input a_root1[4];
    signal input a_root2[4];
    signal input a_root3[4];
    signal input a_root4[4];

    signal input a_evals[72][3];

    signal input a_s0_vals1[32][12];
    signal input a_s0_vals3[32][3];
    signal input a_s0_vals4[32][36];
    signal input a_s0_valsC[32][34];
    signal input a_s0_siblings1[32][24][4];
    signal input a_s0_siblings3[32][24][4];
    signal input a_s0_siblings4[32][24][4];
    signal input a_s0_siblingsC[32][24][4];

    signal input a_s1_root[4];
    signal input a_s2_root[4];
    signal input a_s3_root[4];
    signal input a_s4_root[4];

    signal input a_s1_vals[32][96];
    signal input a_s1_siblings[32][19][4];
    signal input a_s2_vals[32][96];
    signal input a_s2_siblings[32][14][4];
    signal input a_s3_vals[32][96];
    signal input a_s3_siblings[32][9][4];
    signal input a_s4_vals[32][48];
    signal input a_s4_siblings[32][5][4];

    signal input a_finalPol[32][3];



    signal input b_publics[43];

    signal input b_root1[4];
    signal input b_root2[4];
    signal input b_root3[4];
    signal input b_root4[4];
    signal input b_evals[72][3];

    signal input b_s0_vals1[32][12];
    signal input b_s0_vals3[32][3];
    signal input b_s0_vals4[32][36];
    signal input b_s0_valsC[32][34];
    signal input b_s0_siblings1[32][24][4];
    signal input b_s0_siblings3[32][24][4];
    signal input b_s0_siblings4[32][24][4];
    signal input b_s0_siblingsC[32][24][4];

    signal input b_s1_root[4];
    signal input b_s2_root[4];
    signal input b_s3_root[4];
    signal input b_s4_root[4];

    signal input b_s1_vals[32][96];
    signal input b_s1_siblings[32][19][4];
    signal input b_s2_vals[32][96];
    signal input b_s2_siblings[32][14][4];
    signal input b_s3_vals[32][96];
    signal input b_s3_siblings[32][9][4];
    signal input b_s4_vals[32][48];
    signal input b_s4_siblings[32][5][4];

    signal input b_finalPol[32][3];

    component vA = StarkVerifier();

    for (var i=0; i<43; i++) {
        vA.publics[i] <== a_publics[i];
    }
    vA.root1 <== a_root1;
    vA.root2 <== a_root2;
    vA.root3 <== a_root3;
    vA.root4 <== a_root4;
    vA.evals <== a_evals;
    vA.s0_vals1 <== a_s0_vals1;
    vA.s0_vals3 <== a_s0_vals3;
    vA.s0_vals4 <== a_s0_vals4;
    vA.s0_valsC <== a_s0_valsC;
    vA.s0_siblings1 <== a_s0_siblings1;
    vA.s0_siblings3 <== a_s0_siblings3;
    vA.s0_siblings4 <== a_s0_siblings4;
    vA.s0_siblingsC <== a_s0_siblingsC;
    vA.s1_root <== a_s1_root;
    vA.s2_root <== a_s2_root;
    vA.s3_root <== a_s3_root;
    vA.s4_root <== a_s4_root;
    vA.s1_vals <== a_s1_vals;
    vA.s1_siblings <== a_s1_siblings;
    vA.s2_vals <== a_s2_vals;
    vA.s2_siblings <== a_s2_siblings;
    vA.s3_vals <== a_s3_vals;
    vA.s3_siblings <== a_s3_siblings;
    vA.s4_vals <== a_s4_vals;
    vA.s4_siblings <== a_s4_siblings;
    vA.finalPol <== a_finalPol;

    component isOneBatchA = IsZero();
    isOneBatchA.in  <== a_publics[42] - a_publics[16] - 1;
    component a_muxRootC = MultiMux1(4);
    a_muxRootC.c[0] <== rootC;
    a_muxRootC.c[1] <== rootCSingle;
    a_muxRootC.s <== isOneBatchA.out;

    for (var i=0; i<4; i++) {
        vA.publics[43+i] <== rootC[i];
    }
    vA.rootC <== a_muxRootC.out;

    component vB = StarkVerifier();

    for (var i=0; i<43; i++) {
        vB.publics[i] <== b_publics[i];
    }
    vB.root1 <== b_root1;
    vB.root2 <== b_root2;
    vB.root3 <== b_root3;
    vB.root4 <== b_root4;
    vB.evals <== b_evals;
    vB.s0_vals1 <== b_s0_vals1;
    vB.s0_vals3 <== b_s0_vals3;
    vB.s0_vals4 <== b_s0_vals4;
    vB.s0_valsC <== b_s0_valsC;
    vB.s0_siblings1 <== b_s0_siblings1;
    vB.s0_siblings3 <== b_s0_siblings3;
    vB.s0_siblings4 <== b_s0_siblings4;
    vB.s0_siblingsC <== b_s0_siblingsC;
    vB.s1_root <== b_s1_root;
    vB.s2_root <== b_s2_root;
    vB.s3_root <== b_s3_root;
    vB.s4_root <== b_s4_root;
    vB.s1_vals <== b_s1_vals;
    vB.s1_siblings <== b_s1_siblings;
    vB.s2_vals <== b_s2_vals;
    vB.s2_siblings <== b_s2_siblings;
    vB.s3_vals <== b_s3_vals;
    vB.s3_siblings <== b_s3_siblings;
    vB.s4_vals <== b_s4_vals;
    vB.s4_siblings <== b_s4_siblings;
    vB.finalPol <== b_finalPol;


    component isOneBatchB = IsZero();
    isOneBatchB.in <== b_publics[42] - b_publics[16] - 1;
    component b_muxRootC = MultiMux1(4);
    b_muxRootC.c[0] <== rootC;
    b_muxRootC.c[1] <== rootCSingle;
    b_muxRootC.s <== isOneBatchB.out;

    for (var i=0; i<4; i++) {
        vB.publics[43+i] <== rootC[i];
    }
    vB.rootC <== b_muxRootC.out;

    // oldStateRoot
    for (var i=0; i<8; i++) {
        a_publics[i] === publics[i];
    }

    // oldAccInputHash
    for (var i=8; i<16; i++) {
        a_publics[i] === publics[i];
    }

    // oldBatchNum
    a_publics[16] === publics[16];

    // chainId
    a_publics[17] === publics[17];

    // midStateRoot
    for (var i=0; i<8; i++) {
        b_publics[i] === a_publics[18+i];
    }

    // midAccInputHash
    for (var i=8; i<16; i++) {
        b_publics[i] === a_publics[18+i];
    }

    // midBatchNum
    b_publics[16] === a_publics[18+24];

    // chainId
    b_publics[17] === publics[17];


    // newStateRoot
    for (var i=0; i<8; i++) {
        publics[18+i] === b_publics[18+i];
    }

    // newAccInputHash
    for (var i=8; i<16; i++) {
        publics[18+i] === b_publics[18+i];
    }

    // localExitRoot
    for (var i=16; i<24; i++) {
        publics[18+i] === b_publics[18+i];
    }

    // newBatchNum
    publics[18+24] === b_publics[18+24];

}

component main {public [publics, rootC]}= Main();

注:publics 43个元素分别为: 0-7: oldStateRoot; 8-15: OldAccInputHash; 16: oldBatchNum; 17: chainId, 18-25:newStateRoot; 26-33: newAccInputHash; 34-41: localExitRoot, 42: newBatchNum.

生成recursive2 证明时的的 Stark struct 和 recursive1的一致,皆为:

{
    "nBits": 20,
    "nBitsExt": 24,
    "nQueries": 32,
    "verificationHashType": "GL",
    "steps": [
        {"nBits": 24},
        {"nBits": 19},
        {"nBits": 14},
        {"nBits": 9},
        {"nBits": 5}
    ]
}

由于recursive1.pil(生成recursive1 证明)和recursive2.pil(生成recursive2证明)完全一致,并且recursive1.verifier.circom(验证recursive1证明) 和recursive2.verifier.circom` (验证recursive2证明) 一致,

 "recursive_pil_check": ". ./pre.sh && F1=$BDIR/recursive1.pil && F2=$BDIR/recursive2.pil && diff $F1 $F2 || (echo \"ERROR: $F1 $F2 are different\"; exit 1)",
"recursive_verifier_check": ". ./pre.sh && F1=$BDIR/recursive1.verifier.circom && F2=$BDIR/recursive2.verifier.circom && diff $F1 $F2 || (echo \"ERROR: $F1 $F2 are different\"; exit 1)",

因此可以将一个recursive2 证明当作一个recursive1 证明, 可以再次将一个recursive2 证明和一个recursive1 证明,合并成一个新的recursive2 证明。

recursivef

在对recursivef.circom.ejs 进行 ejs 注入时,需要提供recursive1recursive2 的常量根:

    const template = await fs.promises.readFile(path.join(__dirname, "..", "recursive", "recursivef.circom.ejs"), "utf8");

    const obj = {
        constRoot1: constRoot1,
        constRoot2: constRoot2,
    };

    console.log(obj)

    const verifier = ejs.render(template ,  obj);

recursivef.circom.ejs的实现过程为如下,主要是对一个STARK 证明的验证:

pragma circom 2.1.0;
pragma custom_templates;

include "recursive2.verifier.circom";
include "mux1.circom";
include "iszero.circom";

template Main() {
    signal input publics[43];

    signal input root1[4];
    signal input root2[4];
    signal input root3[4];
    signal input root4[4];

    signal input evals[72][3];

    signal input s0_vals1[32][12];
    signal input s0_vals3[32][3];
    signal input s0_vals4[32][36];
    signal input s0_valsC[32][34];
    signal input s0_siblings1[32][24][4];
    signal input s0_siblings3[32][24][4];
    signal input s0_siblings4[32][24][4];
    signal input s0_siblingsC[32][24][4];

    signal input s1_root[4];
    signal input s2_root[4];
    signal input s3_root[4];
    signal input s4_root[4];

    signal input s1_vals[32][96];
    signal input s1_siblings[32][19][4];
    signal input s2_vals[32][96];
    signal input s2_siblings[32][14][4];
    signal input s3_vals[32][96];
    signal input s3_siblings[32][9][4];
    signal input s4_vals[32][48];
    signal input s4_siblings[32][5][4];

    signal input finalPol[32][3];

    component sv = StarkVerifier();

    for (var i=0; i<43; i++) {
        sv.publics[i] <== publics[i];
    }
    sv.root1 <== root1;
    sv.root2 <== root2;
    sv.root3 <== root3;
    sv.root4 <== root4;
    sv.evals <== evals;
    sv.s0_vals1 <== s0_vals1;
    sv.s0_vals3 <== s0_vals3;
    sv.s0_vals4 <== s0_vals4;
    sv.s0_valsC <== s0_valsC;
    sv.s0_siblings1 <== s0_siblings1;
    sv.s0_siblings3 <== s0_siblings3;
    sv.s0_siblings4 <== s0_siblings4;
    sv.s0_siblingsC <== s0_siblingsC;
    sv.s1_root <== s1_root;
    sv.s2_root <== s2_root;
    sv.s3_root <== s3_root;
    sv.s4_root <== s4_root;
    sv.s1_vals <== s1_vals;
    sv.s1_siblings <== s1_siblings;
    sv.s2_vals <== s2_vals;
    sv.s2_siblings <== s2_siblings;
    sv.s3_vals <== s3_vals;
    sv.s3_siblings <== s3_siblings;
    sv.s4_vals <== s4_vals;
    sv.s4_siblings <== s4_siblings;
    sv.finalPol <== finalPol;

    component isOne = IsZero();
    isOne.in <== publics[42] -publics[16] -1;
    component muxKey = MultiMux1(4);
    muxKey.s <== isOne.out;
    muxKey.c[0][0] <== <%- constRoot2[0] %>;
    muxKey.c[0][1] <== <%- constRoot2[1] %>;
    muxKey.c[0][2] <== <%- constRoot2[2] %>;
    muxKey.c[0][3] <== <%- constRoot2[3] %>;
    muxKey.c[1][0] <== <%- constRoot1[0] %>;
    muxKey.c[1][1] <== <%- constRoot1[1] %>;
    muxKey.c[1][2] <== <%- constRoot1[2] %>;
    muxKey.c[1][3] <== <%- constRoot1[3] %>;

    sv.publics[43] <== <%- constRoot2[0] %>;
    sv.publics[44] <== <%- constRoot2[1] %>;
    sv.publics[45] <== <%- constRoot2[2] %>;
    sv.publics[46] <== <%- constRoot2[3] %>;

    sv.rootC[0] <== muxKey.out[0];
    sv.rootC[1] <== muxKey.out[1];
    sv.rootC[2] <== muxKey.out[2];
    sv.rootC[3] <== muxKey.out[3];

}

component main {public [publics]}= Main();

生成 recursivef 证明的Stark struck 结构为:

{
    "nBits": 19,
    "nBitsExt": 23,
    "nQueries": 32,
    "verificationHashType": "BN128",  // 定义在 BN128 标量域上
    "steps": [
        {"nBits": 23},
        {"nBits": 20},
        {"nBits": 16},
        {"nBits": 12},
        {"nBits": 8},
        {"nBits": 4}
    ]
}

final

final.circom 电路主要用于验证recursivef 证明,生成最后的SNARK 证明。

隐私输入为:aggregatorAddr, oldStateRoot, oldAcInputHash, oldBatchNum, chainId, newStateRoot, newAccInputHash, newLocalExitRoot, newBatchNum.

公开输入:上述所有隐私输入变量拼接后计算得到的Hash值。

pragma circom 2.1.0;

/*
aggregatorAddr -> 160   -> 160
oldStateRoot -> 256     -> 416
oldAccInputHash -> 256  -> 672
oldBathcNum -> 64       -> 736
chainId -> 64           -> 800
newStateRoot -> 256     -> 1056
newAccInputHash -> 256  -> 1312
newLocalExitRoot -> 256 -> 1568
newBatchNum -> 64       -> 1632

Total: 1632
*/

include "sha256/sha256.circom";
include "bitify.circom";
include "recursivef.verifier.circom";

template Main() {
    signal output publicsHash;

    signal input aggregatorAddr;

    signal input publics[43];
    signal input root1;
    signal input root2;
    signal input root3;
    signal input root4;

    signal input evals[72][3];

    signal input s0_vals1[32][12];
    signal input s0_vals3[32][3];
    signal input s0_vals4[32][36];
    signal input s0_valsC[32][34];

    signal input s0_siblings1[32][6][16];
    signal input s0_siblings3[32][6][16];
    signal input s0_siblings4[32][6][16];
    signal input s0_siblingsC[32][6][16];

    signal input s1_root;
    signal input s2_root;
    signal input s3_root;
    signal input s4_root;
    signal input s5_root;

    signal input s1_vals[32][24];
    signal input s1_siblings[32][5][16];
    signal input s2_vals[32][48];
    signal input s2_siblings[32][4][16];
    signal input s3_vals[32][48];
    signal input s3_siblings[32][3][16];
    signal input s4_vals[32][48];
    signal input s4_siblings[32][2][16];
    signal input s5_vals[32][48];
    signal input s5_siblings[32][1][16];

    signal input finalPol[16][3];


    component sv = StarkVerifier();
    sv.publics <== publics;
    sv.root1 <== root1;
    sv.root2 <== root2;
    sv.root3 <== root3;
    sv.root4 <== root4;

    sv.evals <== evals;

    sv.s0_vals1 <== s0_vals1;
    sv.s0_vals3 <== s0_vals3;
    sv.s0_vals4 <== s0_vals4;
    sv.s0_valsC <== s0_valsC;

    sv.s0_siblings1 <== s0_siblings1;
    sv.s0_siblings3 <== s0_siblings3;
    sv.s0_siblings4 <== s0_siblings4;
    sv.s0_siblingsC <== s0_siblingsC;

    sv.s1_root <== s1_root;
    sv.s2_root <== s2_root;
    sv.s3_root <== s3_root;
    sv.s4_root <== s4_root;
    sv.s5_root <== s5_root;

    sv.s1_vals <== s1_vals;
    sv.s1_siblings <== s1_siblings;
    sv.s2_vals <== s2_vals;
    sv.s2_siblings <== s2_siblings;
    sv.s3_vals <== s3_vals;
    sv.s3_siblings <== s3_siblings;
    sv.s4_vals <== s4_vals;
    sv.s4_siblings <== s4_siblings;
    sv.s5_vals <== s5_vals;
    sv.s5_siblings <== s5_siblings;

    sv.finalPol <== finalPol;

    component publicsHasher = Sha256(1632);

    component n2bAggregatorAddr = Num2Bits(160);
    n2bAggregatorAddr.in <== aggregatorAddr;
    for (var i=0; i<160; i++) {
        publicsHasher.in[0 + 160 - 1 -i] <== n2bAggregatorAddr.out[i];
    }

    component n2bOldStateRoot[8];
    for (var i=0; i<8; i++) {
        n2bOldStateRoot[i] = Num2Bits(32);
        n2bOldStateRoot[i].in <== publics[0 + i];
        for (var j=0; j<32; j++) {
            publicsHasher.in[160 + 32*(8-i) - 1 -j] <== n2bOldStateRoot[i].out[j];
        }
    }

    component n2bOldAccInputHash[8];
    for (var i=0; i<8; i++) {
        n2bOldAccInputHash[i] = Num2Bits(32);
        n2bOldAccInputHash[i].in <== publics[8 + i];
        for (var j=0; j<32; j++) {
            publicsHasher.in[416 + 32*(8-i) - 1 -j] <== n2bOldAccInputHash[i].out[j];
        }
    }

    // Do 63 bits to avoid aliasing
    component n2bOldBatchNum = Num2Bits(63);
    n2bOldBatchNum.in <== publics[16];
    for (var i=0; i<63; i++) {
        publicsHasher.in[672 + 64 - 1 -i] <== n2bOldBatchNum.out[i];
    }
    publicsHasher.in[672] <== 0;

    component n2bChainId = Num2Bits(63);
    n2bChainId.in <== publics[17];
    for (var i=0; i<63; i++) {
        publicsHasher.in[736 + 64 - 1 -i] <== n2bChainId.out[i];
    }
    publicsHasher.in[736] <== 0;

    component n2bNewStateRoot[8];
    for (var i=0; i<8; i++) {
        n2bNewStateRoot[i] = Num2Bits(32);
        n2bNewStateRoot[i].in <== publics[18+i];
        for (var j=0; j<32; j++) {
            publicsHasher.in[800 + 32*(8-i) - 1 -j] <== n2bNewStateRoot[i].out[j];
        }
    }

    component n2bNewAccInputHash[8];
    for (var i=0; i<8; i++) {
        n2bNewAccInputHash[i] = Num2Bits(32);
        n2bNewAccInputHash[i].in <== publics[26+i];
        for (var j=0; j<32; j++) {
            publicsHasher.in[1056 + 32*(8-i) - 1 -j] <== n2bNewAccInputHash[i].out[j];
        }
    }

    component n2bNewLocalExitRoot[8];
    for (var i=0; i<8; i++) {
        n2bNewLocalExitRoot[i] = Num2Bits(32);
        n2bNewLocalExitRoot[i].in <== publics[34+i];
        for (var j=0; j<32; j++) {
            publicsHasher.in[1312 + 32*(8-i) - 1 -j] <== n2bNewLocalExitRoot[i].out[j];
        }
    }

    component n2bNewBatchNum = Num2Bits(63);
    n2bNewBatchNum.in <== publics[42];
    for (var i=0; i<63; i++) {
        publicsHasher.in[1568 + 64 - 1 -i] <== n2bNewBatchNum.out[i];
    }
    publicsHasher.in[1568] <== 0;

    component b2nPublicsHash = Bits2Num(256);
    for (var i = 0; i < 256; i++) {
        b2nPublicsHash.in[i] <== publicsHasher.out[255-i];
    }

    publicsHash <== b2nPublicsHash.out;
}

component main = Main();

final.circom 编译为R1CS后,即可生成最后SNARK 证明,完成整个递归证明过程。在目前的c++/js 版本的zkevm-prover中,最终生成的还是Groth16证明。

参考

https://polygon.technology/blog/final-approach-last-testnet-for-an-upgraded-polygon-zkevm

https://eips.ethereum.org/EIPS/eip-155

https://github.com/0xPolygonHermez/zkevm-proverjs

https://github.com/0xPolygonHermez/zkevm-prover

上一篇下一篇

猜你喜欢

热点阅读