Polygon zkEVM 递归证明过程

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容