• Polygon zkEVM zkASM语法


    1. 引言

    前序博客有:

    zkASM语法重点参看:

    zkASM程序的基本结构为:

    start(或其它任意label名,序号为0。为主业务流程):
    	assignment : opcode
    
    ; 以分号来表示注释。
    ; 以上主业务流程处理完之后,必须将相关寄存器清零,以防止重入问题。
    ; 该模块的作用是给相关寄存器清零,zkevm-proverjs中的sm_main_exec.js中会`checkFinalState`检查。
    end(或其它任意label名,但必须在后面的补零操作label之前): 
    	0 => A,B,C,D,E,CTX, SP, PC, GAS, MAXMEM, SR
    
    ; 补零操作必须紧跟上面的清零操作。
    padZeros(因execution trace或者说多项式的degree size是固定的,因此需要做补零操作,补零到倒数第二行):
    	notLastTwo : padZerosOp
                   : JMP(start) ;  上面之所以补零到倒数第二行,是因为倒数第一行预留给本指令,以实现跳转到主业务流程功能。
    
    ; 以上操作将整个execution trace表已填满,后续的都是无效指令。
    opInvalid(无效指令,没有意义):
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以zkevm-proverjs中的arith.zkasm为例:

    start:
    
            STEP => A
            0 :ASSERT
    
            0 => A
            CNT_ARITH :ASSERT
            CNT_BINARY :ASSERT
            CNT_KECCAK_F: ASSERT
            CNT_MEM_ALIGN :ASSERT
            CNT_POSEIDON_G :ASSERT
            CNT_PADDING_PG :ASSERT
    
            0 => A,B,C,D    :ARITH
    
            CNT_ARITH => A
            1               :ASSERT
    
            CNT_ARITH => A
            1               :ASSERT
    
            0x2000000000000000000000000000000000000000000000000000000000000001n => A
            0x100    => B
            0x73     => C
            0x20    => D
            0x173   :ARITH
    
    
            2 => A
            CNT_ARITH :ASSERT
    
            0 => A
            CNT_KECCAK_F: ASSERT
            CNT_MEM_ALIGN :ASSERT
            CNT_POSEIDON_G :ASSERT
            CNT_PADDING_PG :ASSERT
    
    end:
           0 => A,B,C,D,E,CTX, SP, PC, GAS, MAXMEM, SR
    
    finalWait:
            ${beforeLast()}  : JMPN(finalWait)
    
                             : JMP(start)
    opINVALID:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    其经zkASM compiler编译后的结果为:【注意,labels中的数值对应上面program数组中的index,其中的JMP/JMPC/JMPN等与offset等配合进行跳转。】

    {
     "program": [
      {
       "inSTEP": "1",
       "setA": 1,
       "line": 3,
       "fileName": "arith.zkasm",
       "lineStr": "        STEP => A"
      },
      {
       "CONST": "0",
       "assert": 1,
       "line": 4,
       "fileName": "arith.zkasm",
       "lineStr": "        0 :ASSERT"
      },
      {
       "CONST": "0",
       "setA": 1,
       "line": 6,
       "fileName": "arith.zkasm",
       "lineStr": "        0 => A"
      },
      {
       "inCntArith": "1",
       "assert": 1,
       "line": 7,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_ARITH :ASSERT"
      },
      {
       "inCntBinary": "1",
       "assert": 1,
       "line": 8,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_BINARY :ASSERT"
      },
      {
       "inCntKeccakF": "1",
       "assert": 1,
       "line": 9,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_KECCAK_F: ASSERT"
      },
      {
       "inCntMemAlign": "1",
       "assert": 1,
       "line": 10,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_MEM_ALIGN :ASSERT"
      },
      {
       "inCntPoseidonG": "1",
       "assert": 1,
       "line": 11,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_POSEIDON_G :ASSERT"
      },
      {
       "inCntPaddingPG": "1",
       "assert": 1,
       "line": 12,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_PADDING_PG :ASSERT"
      },
      {
       "CONST": "0",
       "setA": 1,
       "setB": 1,
       "setC": 1,
       "setD": 1,
       "arith": 1,
       "arithEq0": 1,
       "line": 14,
       "fileName": "arith.zkasm",
       "lineStr": "        0 => A,B,C,D    :ARITH"
      },
      {
       "inCntArith": "1",
       "setA": 1,
       "line": 16,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_ARITH => A"
      },
      {
       "CONST": "1",
       "assert": 1,
       "line": 17,
       "fileName": "arith.zkasm",
       "lineStr": "        1               :ASSERT"
      },
      {
       "inCntArith": "1",
       "setA": 1,
       "line": 19,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_ARITH => A"
      },
      {
       "CONST": "1",
       "assert": 1,
       "line": 20,
       "fileName": "arith.zkasm",
       "lineStr": "        1               :ASSERT"
      },
      {
       "CONSTL": "14474011154664524427946373126085988481658748083205070504932198000989141204993",
       "setA": 1,
       "line": 22,
       "fileName": "arith.zkasm",
       "lineStr": "        0x2000000000000000000000000000000000000000000000000000000000000001n => A"
      },
      {
       "CONST": "256",
       "setB": 1,
       "line": 23,
       "fileName": "arith.zkasm",
       "lineStr": "        0x100    => B"
      },
      {
       "CONST": "115",
       "setC": 1,
       "line": 24,
       "fileName": "arith.zkasm",
       "lineStr": "        0x73     => C"
      },
      {
       "CONST": "32",
       "setD": 1,
       "line": 25,
       "fileName": "arith.zkasm",
       "lineStr": "        0x20    => D"
      },
      {
       "CONST": "371",
       "arith": 1,
       "arithEq0": 1,
       "line": 26,
       "fileName": "arith.zkasm",
       "lineStr": "        0x173   :ARITH"
      },
      {
       "CONST": "2",
       "setA": 1,
       "line": 29,
       "fileName": "arith.zkasm",
       "lineStr": "        2 => A"
      },
      {
       "inCntArith": "1",
       "assert": 1,
       "line": 30,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_ARITH :ASSERT"
      },
      {
       "CONST": "0",
       "setA": 1,
       "line": 32,
       "fileName": "arith.zkasm",
       "lineStr": "        0 => A"
      },
      {
       "inCntKeccakF": "1",
       "assert": 1,
       "line": 33,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_KECCAK_F: ASSERT"
      },
      {
       "inCntMemAlign": "1",
       "assert": 1,
       "line": 34,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_MEM_ALIGN :ASSERT"
      },
      {
       "inCntPoseidonG": "1",
       "assert": 1,
       "line": 35,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_POSEIDON_G :ASSERT"
      },
      {
       "inCntPaddingPG": "1",
       "assert": 1,
       "line": 36,
       "fileName": "arith.zkasm",
       "lineStr": "        CNT_PADDING_PG :ASSERT"
      },
      {
       "CONST": "0",
       "setA": 1,
       "setB": 1,
       "setC": 1,
       "setD": 1,
       "setE": 1,
       "setCTX": 1,
       "setSP": 1,
       "setPC": 1,
       "setGAS": 1,
       "setMAXMEM": 1,
       "setSR": 1,
       "line": 39,
       "fileName": "arith.zkasm",
       "lineStr": "       0 => A,B,C,D,E,CTX, SP, PC, GAS, MAXMEM, SR"
      },
      {
       "freeInTag": {
        "op": "functionCall",
        "funcName": "beforeLast",
        "params": []
       },
       "inFREE": "1",
       "JMPC": 0,
       "JMPN": 1,
       "offset": 27,
       "line": 42,
       "offsetLabel": "finalWait",
       "fileName": "arith.zkasm",
       "lineStr": "        ${beforeLast()}  : JMPN(finalWait)"
      },
      {
       "JMP": 1,
       "JMPC": 0,
       "JMPN": 0,
       "offset": 0,
       "line": 44,
       "offsetLabel": "start",
       "fileName": "arith.zkasm",
       "lineStr": "                         : JMP(start)"
      }
     ],
     "labels": {
      "start": 0,
      "end": 26,
      "finalWait": 27,
      "opINVALID": 29
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240

    1.1 zkASM计数器

    zkASM语言支持的计数器counter有:

    计数器名说明备注
    CNT_ARITH{ $$ = ‘cntArith’ }为调用Arithmetic状态机计数器。针对的操作符有:ARITH、ARITH_ECADD_DIFFERENT、ARITH_ECADD_SAME。
    CNT_BINARY{ $$ = ‘cntBinary’ }为调用Binary状态机计数器。针对的操作符有:ADD、SUB、LT、SLT、EQ、AND、OR、XOR。
    CNT_KECCAK_F{ $$ = ‘cntKeccakF’ }为调用Keccak哈希状态机计数器。针对的操作符有:HASHKDIGEST。计数单位为incCounter
    CNT_MEM_ALIGN{ $$ = ‘cntMemAlign’ }为调用Memory Align状态机计数器。针对的操作符有:MEM_ALIGN_RD、MEM_ALIGN_WR、MEM_ALIGN_WR8。
    CNT_PADDING_PG{ $$ = ‘cntPaddingPG’ }为调用Poseidon Padding_pg状态机计数器。针对的操作符有:HASHPDIGEST。计数单位为incCounter
    CNT_POSEIDON_G{ $$ = ‘cntPoseidonG’ }为调用Poseidon Poseidong状态机计数器。针对的操作符有:HASHDIGEST、SLOAD、SSTORE。计数单位为incCounter

    1.2 zkASM scope

    zkASM语言支持2种scope

    scope标志说明备注
    GLOBAL
    CTX

    2. zkASM statement基本类型定义

    • IDENTIFIER格式为:[a-zA-Z_][a-zA-Z$_0-9]* ,表示由字母和数字组成。
    • setLine函数定义为:
      function setLine(dst, first) {
          dst.line = first.first_line;
      }
      
      • 1
      • 2
      • 3
    • NUMBERL:为BigInt,对应CONSTL。
    • NUMBER:为NUMBER,对应CONST。

    zkASM语言支持的statement类型有:

    statement类型说明备注
    step{
    $$ = $1;
    }
    label{
    $$ = $1;
    }
    varDef{
    $$ = $1;
    }
    constDef{
    $$ = $1;
    }
    include{
    $$ = $1;
    }
    command{
    $$ = $1;
    }
    LF{
    $$ = null;
    }

    基于statement构建的statementList为:

    statementList类型说明备注
    statmentList statment{
    if ($2) $1.push($2);
    $$ = $1;
    }
    statment{
    if ($1) {
    $$ = [$1];
    } else {
    $$=[];
    }
    }

    基于statementList构建的allStatments为:

    allStatments类型说明备注
    statmentList EOF{
    // console.log($1);
    $$ = $1;
    return $$;
    }

    2.1 zkASM statement step类型

    zkASM语言支持的statement step类型有:

    step标志说明备注
    assignment ‘:’ opList LF{
    $$ = {type: "step", assignment: $1, ops: $3};
    setLine($$, @1)
    }
    assignment LF{
    $$ = {type: "step", assignment: $1, ops: []};
    setLine($$, @1)
    }
    ‘:’ opList LF{
    $$ = {type: "step", assignment: null, ops: $2}
    setLine($$, @1)
    }

    2.2 zkASM statement label类型

    zkASM语言支持的statement label类型有:

    label标志说明备注
    IDENTIFIER ‘:’{
    $$ = {type: "label", identifier: $1};
    setLine($$, @1)
    }

    2.3 zkASM statement varDef类型

    zkASM语言支持的statement varDef类型有:

    varDef标志说明备注
    VAR scope IDENTIFIER{
    $$ = {type: “var”, scope: $2, name: $3, count: 1 }
    }
    VAR scope IDENTIFIER ‘[’ NUMBER ‘]’{
    $$ = {type: “var”, scope: $2, name: $3, count: $5 }
    }

    2.4 zkASM statement constDef类型

    zkASM语言支持的statement constDef类型有:

    constDef标志说明备注
    ‘CONST’ CONSTID ‘=’ nexpr %prec ‘=’{
    $$ = {type: "constdef", name: $2, value: $4}
    setLine($$, @1);
    }
    ‘CONSTL’ CONSTID ‘=’ nexpr %prec ‘=’{
    $$ = {type: "constldef", name: $2, value: $4}
    setLine($$, @1);
    }

    其中的nexpr类型有:

    nexpr标志说明备注
    NUMBER{
    $$ = {type: ‘CONSTL’ , value: $1}
    }
    NUMBERL{
    $$ = {type: ‘CONSTL’ , value: $1}
    }
    CONSTID{
    $$ = {type: ‘CONSTID’ , identifier: $1}
    }
    CONSTID ‘??’ nexpr{
    $$ = {type: $2, values: [$3] , identifier: $1}
    }
    nexpr ‘+’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘-’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘*’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘**’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘%’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘/’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    ‘-’ nexpr{
    $$ = {type: $1, values: [$2]}
    }
    nexpr ‘<<’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘>>’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘|’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘&’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘^’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘<’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘>’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘<=’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘>=’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘==’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘!=’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘&&’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    nexpr ‘||’ nexpr{
    $$ = {type: $2, values: [$1, $3]}
    }
    ‘!’ nexpr{
    $$ = {type: $1, values: [$2]}
    }
    nexpr ‘?’ nexpr ‘:’ nexpr{
    $$ = {type: $2, values: [$1, $3, $5]}
    }
    ‘(’ nexpr ‘)’{
    $$ = $2
    }

    2.5 zkASM statement include类型

    zkASM语言支持的statement include类型有:

    include标志说明备注
    INCLUDE STRING{
    $$ = {type: “include”, file: $2}
    }

    2.6 zkASM statement command类型

    zkASM语言支持的statement command类型有:

    command标志说明备注
    COMMAND{
    $$ = {type: “command”, cmd: $1}
    }

    2.6.1 zkASM command中的leftExpression

    zkASM语言command中支持的leftExpression有:

    leftExpression标志说明备注
    VAR IDENTIFIER{
    $$ = {op: “declareVar”, varName: $2}
    }
    IDENTIFIER{
    $$ = {op: “getVar”, varName: $1}
    }

    2.6.2 zkASM command中的e0

    zkASM语言command中支持的e0有:

    e0标志说明备注
    leftExpression{
    $$ = $1
    }
    NUMBER{
    $$ = {op: “number”, num: $1 }
    }
    reg{
    $$ = {op: “getReg”, regName: $1}
    }
    counter{
    $$ = {op: “getReg”, regName: $1}
    }
    ‘(’ expression ‘)’{
    $$ = $2;
    }
    IDENTIFIER ‘.’ IDENTIFIER{
    $$ = {op: “getData”, module: $1, offset: $3}
    }

    2.6.3 zkASM command中的e1

    zkASM语言command中支持的e1有:

    e1标志说明备注
    functionCall{
    $$ = $1;
    }
    e0{
    $$ = $1
    }

    2.6.4 zkASM command中的e2

    zkASM语言command中支持的e2有:

    e2标志说明备注
    ‘+’ e2 %prec UPLUS{
    $$ = $2;
    }
    ‘-’ e2 %prec UMINUS{
    $$ = { op: “neg”, values: [$2] };
    }
    ‘~’ e2 %prec NOT{
    $$ = { op: “bitnot”, values: [$2] };
    }
    ‘!’ e2 %prec NOT{
    $$ = { op: “not”, values: [$2] };
    }
    e1 %prec EMPTY{
    $$ = $1;
    }

    2.6.5 zkASM command中的e3

    zkASM语言command中支持的e3有:

    e3标志说明备注
    e3 ‘*’ e2{
    $$ = { op: “mul”, values: [$1, $3] };
    }
    e3 ‘/’ e2{
    $$ = { op: “div”, values: [$1, $3] };
    }
    e3 ‘%’ e2{
    $$ = { op: “mod”, values: [$1, $3] };
    }
    e3 ‘&’ e2{
    $$ = { op: “bitand”, values: [$1, $3] };
    }
    e3 ‘|’ e2{
    $$ = { op: “bitor”, values: [$1, $3] };
    }
    e3 ‘^’ e2{
    $$ = { op: “bitxor”, values: [$1, $3] };
    }
    e3 SHL e2{
    $$ = { op: “shl”, values: [$1, $3] };
    }
    e3 SHR e2{
    $$ = { op: “shr”, values: [$1, $3] };
    }
    e3 L_OR e2{
    $$ = { op: “or”, values: [$1, $3] };
    }
    e3 L_AND e2{
    $$ = { op: “and”, values: [$1, $3] };
    }
    e3 EXP e2{
    $$ = { op: “exp”, values: [$1, $3] };
    }
    e3 EQ e2{
    $$ = { op: “eq”, values: [$1, $3] };
    }
    e3 NE e2{
    $$ = { op: “ne”, values: [$1, $3] };
    }
    e3 LT e2{
    $$ = { op: “lt”, values: [$1, $3] };
    }
    e3 LE e2{
    $$ = { op: “le”, values: [$1, $3] };
    }
    e3 GT e2{
    $$ = { op: “gt”, values: [$1, $3] };
    }
    e3 GE e2{
    $$ = { op: “ge”, values: [$1, $3] };
    }
    e3 ‘?’ e2 ‘:’ e2{
    $$ = { op: “if”, values: [$1, $3, $5] };
    }
    e2 %prec EMPTY{
    $$ = $1;
    }

    2.6.6 zkASM command中的e4

    zkASM语言command中支持的e4有:

    e4标志说明备注
    e4 ‘+’ e3{
    $$ = { op: “add”, values: [$1, $3] };
    }
    e4 ‘-’ e3{
    $$ = { op: “sub”, values: [$1, $3] };
    }
    e3 %prec EMPTY{
    $$ = $1;
    }

    2.6.7 zkASM command中的e5

    zkASM语言command中支持的e5有:

    e5标志说明备注
    leftExpression ‘=’ e5{
    $$ = { op: “setVar”, values: [$1, $3] };
    }
    e4 %prec EMPTY{
    $$ = $1;
    }

    2.6.8 zkASM command中的expression

    zkASM语言command中支持的expression有:

    expression标志说明备注
    e5 %prec EMPTY{
    $$ = $1;
    }

    基于expression构建的tag为:

    tag标志说明备注
    expression EOF{
    // console.log($1);
    $$ = $1;
    return $$;
    }

    基于expression构建的expressionList为:

    expressionList标志说明备注
    expressionList ‘,’ expression{
    $1.push($3);
    }
    expression %prec ‘,’{
    $$ = [$1];
    }

    基于expressionList构建的functionCall为:

    functionCall标志说明备注
    IDENTIFIER ‘(’ expressionList ‘)’{
    $$ = {op: “functionCall”, funcName: $1, params: $3}
    }
    IDENTIFIER ‘(’ ‘)’{
    $$ = {op: “functionCall”, funcName: $1, params: []}
    }

    3. zkASM寄存器

    zkASM语言支持的寄存器reg有:

    寄存器名说明备注
    AASSERT操作符仅作用于A寄存器B : ASSERT,表示断言A寄存器中的值与B寄存器中的值相等
    B
    C
    D
    E
    SRStorage Merkle tree State Root寄存器SLOAD和SSTORE会对该寄存器代表的sparse merkle tree进行读写。
    CTX
    SPStack pointer
    PCProgram counter为实现有条件跳转,引入PC(Program Counter),记录了程序执行的当前指令的位置。
    GAS
    RR与zkPC寄存器配合使用,控制子程序跳转和返回。如zkPC+1 => RR :JMP(subroutine) ; 等效为CALL(subroutime):RETURN ; 等效为 :JMP(RR)
    zkPCZero-knowledge program counter
    STEP只读寄存器。number of polynomial evaluation
    MAXMEM
    HASHPOS
    ROTL_C只读寄存器,仅作用于C寄存器,将其4个字节左移举例为:
    0x101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2Fn => C
    ROTL_C => A
    0x1415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F10111213n: ASSERT

    zkASM语言的寄存器列表regsList表示为:

    寄存器列表标志说明备注
    regsList ‘,’ reg{
    $1.push($3)
    }
    reg{
    $$ = [$1]
    }

    3.1 zkASM 寄存器赋值

    zkASM语言支持的寄存器赋值操作assignment有:

    assignment标志说明备注
    inRegsSum ‘=>’ regsList{
    $$ = {in: $1, out: $3}
    }
    inRegsSum{
    $$ = {in: $1, out: []} }

    其中inRegsSum中支持的运算类型有:

    inRegsSum运算标志说明备注
    inRegsSum ‘+’ inRegP{
    $$ = {type: ‘add’, values: [$1, $3]}
    }
    inRegsSum ‘-’ inRegP{
    $$ = {type: ‘sub’, values: [$1, $3]}
    }
    ‘-’ inRegP{
    $$ = {type: ‘neg’, values: [$2]}
    }
    inRegP{
    $$ = $1
    }

    其中inRegP中支持的运算类型有:

    inRegP运算标志说明备注
    inRegP ‘*’ inReg{
    $$ = {type: ‘mul’, values: [$1, $3]}
    }
    inReg{
    $$ = $1
    }

    其中inReg中支持的运算类型有:

    inReg运算标志说明备注
    TAG{
    $$ = {type: ‘TAG’ , tag: $1}
    }
    reg{
    $$ = {type: ‘REG’ , reg: $1}
    }
    counter{
    $$ = {type: ‘COUNTER’, counter: $1}
    }
    NUMBER ‘**’ NUMBER{
    $$ = {type: “exp”, values: [$1, $3]}
    }
    NUMBER{
    $$ = {type: ‘CONST’ , const: $1}
    }
    NUMBERL{
    $$ = {type: ‘CONSTL’ , const: $1}
    }
    CONSTID{
    $$ = {type: ‘CONSTID’ , identifier: $1}
    }
    REFERENCE{
    $$ = {type: ‘reference’, identifier: $1}
    }

    4. zkASM操作符

    Rom状态机内包含了如下所有操作符的相应常量多项式。

    zkASM语言支持的操作符op有:

    操作符名对应列说明备注
    MLOAD ‘(’ addr ‘)’{
    $$ = $3;
    $$.mOp = 1;
    $$.mWR = 0;
    }
    使用Memory状态机。为32-byte word内存读操作。
    MSTORE ‘(’ addr ‘)’{
    $$ = $3;
    $$.mOp = 1;
    $$.mWR = 1;
    }
    使用Memory状态机。为32-byte word内存写操作。
    HASHK ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashK = 1;
    }
    若hashId为Number,则addr=Number;若为E,则addr=E(0)。将A寄存器中ctx.D[0]个字节附加到 ctx.hashK[addr].data 中ctx.HASHPOS位置之后(若 ctx.hashK[addr].data相应位置有值,则不覆盖,仅在无值位置附加)。若inFree为1,则取ctx.hashK[addr].data中ctx.HASHPOS往后的ctx.D[0]个字节。
    HASHKLEN ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashKLen = 1;
    }
    若hashId为Number,则addr=Number;若为E,则addr=E(0)。HASHKLEN为对ctx.hashK[addr].data进行keccak256哈希运算,结果存在ctx.hashK[addr].digest中。
    HASHKDIGEST ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashKDigest = 1;
    }
    若hashId为Number,则addr=Number;若为E,则addr=E(0)。返回ctx.hashK[addr].digest哈希值。
    HASHP ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashP = 1;
    }
    HASHPLEN ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashPLen = 1;
    }
    HASHPDIGEST ‘(’ hashId ‘)’{
    $$ = $3;
    $$.hashPDigest = 1;
    }
    JMP ‘(’ IDENTIFIER ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, offset: $3}
    }
    JMP ‘(’ RR ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, ind: 0, indRR: 1, offset: 0}
    }
    JMP ‘(’ E ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, ind: 1, indRR: 0, offset: 0}
    }
    JMP ‘(’ REFERENCE ‘+’ RR ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, ind: 0, indRR: 1, offset: $3}
    }
    JMP ‘(’ REFERENCE ‘+’ E ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, ind: 1, indRR: 0, offset: $3}
    }
    JMPC ‘(’ IDENTIFIER ‘)’{
    $$ = {JMPC: 1, JMPN: 0, offset: $3}
    }
    JMPN ‘(’ IDENTIFIER ‘)’{
    $$ = {JMPC: 0, JMPN: 1, offset: $3}
    }
    CALL ‘(’ IDENTIFIER ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, offset: $3, assignment: { in: {type: ‘add’, values: [{type: ‘REG’, reg: ‘zkPC’}, {type: ‘CONST’, const: 1}] }, out:[‘RR’]}}
    }
    CALL(subroutine)等效为zkPC+1 => RR :JMP(subroutine)
    CALL ‘(’ REFERENCE ‘+’ RR ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, offset: $3, ind: 0, indRR: 1, assignment: { in: {type: ‘add’, values: [{type: ‘REG’, reg: ‘zkPC’}, {type: ‘CONST’, const: 1}] }, out:[‘RR’]}}
    }
    CALL ‘(’ REFERENCE ‘+’ E ‘)’{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, offset: $3, ind: 1, indRR: 0, assignment: { in: {type: ‘add’, values: [{type: ‘REG’, reg: ‘zkPC’}, {type: ‘CONST’, const: 1}] }, out:[‘RR’]}}
    }
    JMPC ‘(’ RR ‘)’{
    $$ = {JMP: 0, JMPC: 1, JMPN: 0, ind: 0, indRR: 1, offset: 0}
    }
    JMPC ‘(’ E ‘)’{
    $$ = {JMP: 0, JMPC: 1, JMPN: 0, ind: 1, indRR: 0, offset: 0}
    }
    JMPC ‘(’ REFERENCE ‘+’ RR ‘)’{
    $$ = {JMP: 0, JMPC: 1, JMPN: 0, ind: 0, indRR: 1, offset: $3}
    }
    JMPC ‘(’ REFERENCE ‘+’ E ‘)’{
    $$ = {JMP: 0, JMPC: 1, JMPN: 0, ind: 1, indRR: 0, offset: $3}
    }
    JMPN ‘(’ RR ‘)’{
    $$ = {JMP: 0, JMPC: 0, JMPN: 1, ind: 0, indRR: 1, offset: 0}
    }
    JMPN ‘(’ E ‘)’{
    $$ = {JMP: 0, JMPC: 0, JMPN: 1, ind: 1, indRR: 0, offset: 0}
    }
    JMPN ‘(’ REFERENCE ‘+’ RR ‘)’{
    $$ = {JMP: 0, JMPC: 0, JMPN: 1, ind: 0, indRR: 1, offset: $3}
    }
    JMPN ‘(’ REFERENCE ‘+’ E ‘)’{
    $$ = {JMP: 0, JMPC: 0, JMPN: 1, ind: 1, indRR: 0, offset: $3}
    }
    RETURN{
    $$ = {JMP: 1, JMPC: 0, JMPN: 0, ind: 0, indRR: 1, offset: 0}
    }
    ASSERT{
    $$ = {assert: 1}
    }
    ECRECOVER{
    $$ = {ecRecover: 1}
    }
    SLOAD{
    $$ = {sRD: 1}
    }
    Storage读操作。读取SMT中,以(A/B)C寄存器值为key的值。
    SSTORE{
    $$ = {sWR: 1}
    }
    Storage写操作。将D寄存器的值存储到SMT((A/B)C寄存器值相关的)位置中。若inFree为1,同时还返回写操作之后的smt new root。
    ARITH{
    $$ = { arith: 1, arithEq0: 1}
    }
    使用Arithmetic状态机。计算EQ0: x 1 ⋅ y 1 + x 2 − y 2 ⋅ 2 256 − y 3 = 0 x_1\cdot y_1+x_2-y_2\cdot 2^{256}-y_3=0 x1y1+x2y22256y3=0。实际为 A ⋅ B + C = D ⋅ 2 256 + E A\cdot B+C=D\cdot 2^{256}+E AB+C=D2256+E
    ARITH_ECADD_DIFFERENT{
    $$ = { arith: 1, arithEq1: 1, arithEq3: 1}
    }
    使用Arithmetic状态机。计算椭圆曲线上不同的2个点之和。
    ARITH_ECADD_SAME{
    $$ = { arith: 1, arithEq2: 1, arithEq3: 1}
    }
    使用Arithmetic状态机。计算椭圆曲线上相同的2个点之和。
    SHL{
    $$ = { shl: 1}
    }
    SHR{
    $$ = { shr: 1}
    }
    ADD{
    $$ = { bin: 1, binOpcode: 0}
    }
    使用Binary状态机。byte-wise加法运算。
    SUB{
    $$ = { bin: 1, binOpcode: 1}
    }
    使用Binary状态机。byte-wise减法运算。
    LT{
    $$ = { bin: 1, binOpcode: 2}
    }
    使用Binary状态机。byte-wise小于运算。
    SLT{
    $$ = { bin: 1, binOpcode: 3}
    }
    使用Binary状态机。byte-wise有符号小于运算。
    EQ{
    $$ = { bin: 1, binOpcode: 4}
    }
    使用Binary状态机。byte-wise等于运算。
    AND{
    $$ = { bin: 1, binOpcode: 5}
    }
    使用Binary状态机。bit-wise位与运算。
    OR{
    $$ = { bin: 1, binOpcode: 6}
    }
    使用Binary状态机。bit-wise位或运算。
    XOR{
    $$ = { bin: 1, binOpcode: 7}
    }
    使用Binary状态机。bit-wise位异或运算。
    MEM_ALIGN_RD{
    $$ = { memAlign: 1, memAlignWR: 0, memAlignWR8: 0}
    }
    使用Memory Align状态机。读取指定offset开始的32 byte内存值。
    MEM_ALIGN_WR{
    $$ = { memAlign: 1, memAlignWR: 1, memAlignWR8: 0}
    }
    使用Memory Align状态机。写入指定offset开始的32 byte内存值。
    MEM_ALIGN_WR8{
    $$ = { memAlign: 1, memAlignWR: 0, memAlignWR8: 1}
    }
    使用Memory Align状态机。写入指定offset开始的1 byte内存值。
    INST_MAP_ROM{
    $$ = {instMapRom: 1}
    }

    其中HASHK/HASHKLEN/HASHKDIGEST/HASHP/HASHPLEN/HASHPDIGEST中的参数hashId的类型有:

    hashId取值类型对应列说明备注
    NUMBER{
    $$ = {ind: 0, indRR: 0, offset:$1}
    }
    E{
    $$ = {ind: 1, indRR: 0, offset:0}
    }

    sm_main_exec.jsexecute函数中的incCounter具体取值为:

    • 1)对于HASHPDIGEST操作符为对56取模:incCounter = Math.ceil((ctx.hashP[addr].data.length + 1) / 56);
    • 2)对于HASHKDIGEST操作符为对136取模:incCounter = Math.ceil((ctx.hashK[addr].data.length + 1) / 136)
    • 3)对于SLOAD和SSTORE操作符为:incCounter = res.proofHashCounter + 2;,其中proofHashCounter为 证明该操作所需的哈希次数:
      const res = await smt.get(sr8to4(ctx.Fr, ctx.SR), key);
      /**
       * Get value merkle-tree
       * @param {Array[Field]} root - merkle-tree root
       * @param {Array[Field]} key - path to retoreve the value
       * @returns {Object} Information about the value to retrieve
       *      {Array[Field]} root: merkle-tree root,
       *      {Array[Field]} key: key to look for,
       *      {Scalar} value: value retrieved,
       *      {Array[Array[Field]]} siblings: array of siblings,
       *      {Bool} isOld0: is new insert or delete,
       *      {Array[Field]} insKey: key found,
       *      {Scalar} insValue: value found,
       *      {Number} proofHashCounter: counter of hashs must be done to proof this operation
       */
      async get(root, key) {
          const self = this;
          const { F } = this;
      
          let r = root;
      
          const keys = self.splitKey(key);
          let level = 0;
      
          const accKey = [];
          let foundKey;
          let siblings = [];
      
          let insKey;
          let insValue = Scalar.e(0);
      
          let value = Scalar.e(0);
          let isOld0 = true;
      
          let foundVal;
      
          while ((!nodeIsZero(r, F)) && (typeof (foundKey) === 'undefined')) {
              siblings[level] = await self.db.getSmtNode(r);
              if (isOneSiblings(siblings[level], F)) {
                  const foundValA = (await self.db.getSmtNode(siblings[level].slice(4, 8))).slice(0, 8);
                  const foundRKey = siblings[level].slice(0, 4);
                  foundVal = fea2scalar(F, foundValA);
                  foundKey = this.joinKey(accKey, foundRKey);
              } else {
                  r = siblings[level].slice(keys[level] * 4, keys[level] * 4 + 4);
                  accKey.push(keys[level]);
                  level += 1;
              }
          }
      
          level -= 1;
          accKey.pop();
      
          if (typeof (foundKey) !== 'undefined') {
              if (nodeIsEq(key, foundKey, F)) {
                  value = foundVal;
              } else {
                  insKey = foundKey;
                  insValue = foundVal;
                  isOld0 = false;
              }
          }
      
          siblings = siblings.slice(0, level + 1);
      
          return {
              root,
              key,
              value,
              siblings,
              isOld0,
              insKey,
              insValue,
              proofHashCounter: nodeIsZero(root, F) ? 0 : (siblings.length + ((F.isZero(value) && isOld0 !== false) ? 0 : 2)),
          };
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76

    zkASM操作符列表opList表示为:

    操作符列表标志说明备注
    opList ‘,’ op{
    $1.push($3);
    $$ = $1
    }
    op{
    $$ = [$1]
    }

    4.1 Poseidon哈希运算

    Polygon zkEVM Poseidon哈希运算相关操作符和寄存器有:

    • 1)寄存器HASHPOS:对应Rom状态机内的setHASHPOS常量多项式。
    • 2)操作符HASHP(hashId):以HASH(E)为例,对应Rom状态机内的hashP、ind、indRR、offset常量多项式。
    • 3)操作符HASHPLEN(hashId):以HASHPLEN(E)为例,对应Rom状态机内的hashPLen、ind、indRR、offset常量多项式。
    • 4)操作符HASHPDIGEST(hashId):以HASHPDIGEST(E)为例,对应Rom状态机内的hashPDigest、ind、indRR、offset常量多项式。

    这些多项式之间的逻辑关系见zkevm-proverjs/src/sm/sm_main/sm_main_exec.js中的execute函数:

    • 1)HASHPOS寄存器对应的setHASHPOS常量多项式计算逻辑为:
      // 1. 在每一步中,将incHashPos初始化为0
      let incHashPos = 0;
      // 2. 在当前步中有hashP或hashK时,将D寄存器D[0]值给incHashPos
      const size = fe2n(Fr, ctx.D[0], ctx);
      incHashPos = size;
      const pos = fe2n(Fr, ctx.HASHPOS, ctx);
      console.log('ZYD i:' + i + ', hashP size:' + size + ', pos:' + pos);
      
      if (l.setHASHPOS == 1) {
      	console.log('ZYD i:' + i + ', hashP size:' + size + ', pos:' + pos);
      	pols.setHASHPOS[i]=1n;
          pols.HASHPOS[nexti] = BigInt(fe2n(Fr, op0, ctx) + incHashPos);
      } else {
          pols.setHASHPOS[i]=0n;
          pols.HASHPOS[nexti] = pols.HASHPOS[i] + BigInt( incHashPos);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      test/counters/padding_pg.js为例,相应打印日志为:
      **** ZYD i:10, op0:0, incHashPos:0
      ZYD i:12, hashP size:32, pos:0
      ZYD i:14, hashP size:23, pos:32
      **** ZYD i:23, op0:0, incHashPos:0
      ZYD i:25, hashP size:32, pos:0
      ZYD i:27, hashP size:24, pos:32
      **** ZYD i:34, op0:0, incHashPos:0
      ZYD i:36, hashP size:32, pos:0
      ZYD i:38, hashP size:25, pos:32
      **** ZYD i:45, op0:0, incHashPos:0
      ZYD i:47, hashP size:32, pos:0
      ZYD i:48, hashP size:32, pos:32
      ZYD i:49, hashP size:32, pos:64
      ZYD i:51, hashP size:15, pos:96
      **** ZYD i:58, op0:0, incHashPos:0
      ZYD i:60, hashP size:32, pos:0
      ZYD i:61, hashP size:32, pos:32
      ZYD i:62, hashP size:32, pos:64
      ZYD i:64, hashP size:16, pos:96
      **** ZYD i:71, op0:0, incHashPos:0
      ZYD i:73, hashP size:32, pos:0
      ZYD i:74, hashP size:32, pos:32
      ZYD i:75, hashP size:32, pos:64
      ZYD i:77, hashP size:17, pos:96
      **** ZYD i:84, op0:0, incHashPos:0
      ZYD i:86, hashP size:32, pos:0
      ZYD i:87, hashP size:32, pos:32
      ZYD i:88, hashP size:32, pos:64
      ZYD i:90, hashP size:18, pos:96
      **** ZYD i:101, op0:0, incHashPos:0
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

    4.2 JMP/CALL/JMPN/JMPC/RETURN有条件跳转

    具体参看:

    • zkASM Compiler项目中的c_code_generator.js文件
      	if (rom.program[zkPC].mOp || rom.program[zkPC].mWR || rom.program[zkPC].hashK || rom.program[zkPC].hashKLen || rom.program[zkPC].hashKDigest || rom.program[zkPC].hashP || rom.program[zkPC].hashPLen || rom.program[zkPC].hashPDigest || rom.program[zkPC].JMP || rom.program[zkPC].JMPN || rom.program[zkPC].JMPC)
          {
              let bAddrRel = false;
              let bOffset = false;
              code += "    // If address is involved, load offset into addr\n";
              if (rom.program[zkPC].ind && rom.program[zkPC].indRR)
              {
                  console.log("Error: Both ind and indRR are set to 1");
                  process.exit();
              }
              if (rom.program[zkPC].ind)
              {
                  code += "    fr.toS32(addrRel, pols.E0[" + (bFastMode?"0":"i") + "]);\n";
                  bAddrRel = true;
              }
              if (rom.program[zkPC].indRR)
              {
                  code += "    fr.toS32(addrRel, pols.RR[" + (bFastMode?"0":"i") + "]);\n";
                  bAddrRel = true;
              }
              if (rom.program[zkPC].offset && (rom.program[zkPC].offset != 0))
              {
                  bOffset = true;
              }
              if (bAddrRel && bOffset)
              {
                  if (rom.program[zkPC].offset > 0)
                  {
                  code += "    // If offset is possitive, and the sum is too big, fail\n"
                  code += "    if ((uint64_t(addrRel)+uint64_t(" + rom.program[zkPC].offset +  "))>=0x10000)\n"
                  code += "    {\n"
                  code += "        cerr << \"Error: addrRel >= 0x10000 ln: \" << " + zkPC + " << endl;\n"
                  code += "        exitProcess();\n"
                  code += "    }\n"
                  }
                  else // offset < 0
                  {
                  code += "    // If offset is negative, and its modulo is bigger than addrRel, fail\n"
                  code += "    if (" + (-rom.program[zkPC].offset) + ">addrRel)\n"
                  code += "    {\n"
                  code += "        cerr << \"Error: addrRel < 0 ln: \" << " + zkPC + " << endl;\n"
                  code += "        exitProcess();\n"
                  code += "    }\n"
                  }
                  code += "    addrRel += " + rom.program[zkPC].offset + ";\n"
                  code += "    addr = addrRel;\n\n";
              }
              else if (bAddrRel && !bOffset)
              {
                  code += "    addr = addrRel;\n\n"; // TODO: Check that addrRel>=0 and <0x10000, if this is as designed
              }
              else if (!bAddrRel && bOffset)
              {
                  if ((rom.program[zkPC].offset < 0) || (rom.program[zkPC].offset >= 0x10000))
                  {
                      console.log("Error: invalid offset=" + rom.program[zkPC].offset);
                      program.exit();
                  }
                  if (!bFastMode)
                      code += "    addrRel = " + rom.program[zkPC].offset + ";\n";
                  code += "    addr = " + rom.program[zkPC].offset + ";\n\n";
              }
              else if (!bAddrRel && !bOffset)
              {
                  if (!bFastMode)
                      code += "    addrRel = 0;\n";
                  code += "    addr = 0;\n\n";
              }
          }
          
      	/*********/
          /* JUMPS */
          /*********/
      
          // If JMPN, jump conditionally if op0<0
          if (rom.program[zkPC].JMPN)
          {
              if (!bFastMode)
                  code += "    pols.JMPN[i] = fr.one();\n";
              code += "    fr.toS32(o, op0);\n"
              // If op<0, jump to addr: zkPC'=addr
              code += "    if (o < 0) {\n";
              if (!bFastMode)
              {
                  code += "        pols.isNeg[i] = fr.one();\n";
                  code += "        pols.zkPC[nexti] = fr.fromU64(addr); // If op<0, jump to addr: zkPC'=addr\n";
                  code += "        required.Byte4[0x100000000 + o] = true;\n";
              }
              //code += "        goto *" + functionName + "_labels[addr]; // If op<0, jump to addr: zkPC'=addr\n";
              code += "        bJump = true;\n";
              bConditionalJump = true;
              code += "    }\n";
              // If op>=0, simply increase zkPC'=zkPC+1
              code += "    else\n";
              code += "    {\n";
              if (!bFastMode)
              {
                  code += "        pols.zkPC[nexti] = fr.add(pols.zkPC[i], fr.one()); // If op>=0, simply increase zkPC'=zkPC+1\n";
                  code += "        required.Byte4[o] = true;\n";
              }
              code += "        addr = " + (zkPC+1) + ";\n";
              //code += "        goto RomLine" + (zkPC+1) + ";\n";
              code += "    }\n";
          }
          // If JMPC, jump conditionally if carry
          else if (rom.program[zkPC].JMPC)
          {
              if (!bFastMode)
                  code += "    pols.JMPC[i] = fr.one();\n";
              code += "    if (!fr.isZero(pols.carry[" + (bFastMode?"0":"i") + "]))\n";
              code += "    {\n";
              if (!bFastMode)
                  code += "        pols.zkPC[nexti] = fr.fromU64(addr); // If carry, jump to addr: zkPC'=addr\n";
              bConditionalJump = true;
              code += "        bJump = true;\n";
              if (bFastMode) // We reset the global variable to prevent jumping in next zkPC
                  code += "        pols.carry[0] = fr.zero();\n";
              code += "    }\n";
              if (!bFastMode)
              {
                  code += "    else\n";
                  code += "{\n";
                  code += "        pols.zkPC[nexti] = fr.add(pols.zkPC[i], fr.one()); // If not carry, simply increase zkPC'=zkPC+1\n";
                  code += "}\n";
              }
          }
          // If JMP, directly jump zkPC'=addr
          else if (rom.program[zkPC].JMP)
          {
              if (!bFastMode)
              {
                  code += "    pols.zkPC[nexti] = fr.fromU64(addr);\n";
                  code += "    pols.JMP[i] = fr.one();\n";
              }
              //code += "    goto *" + functionName + "_labels[addr]; // If JMP, directly jump zkPC'=addr\n";
              bForcedJump = true;
              //code += "    bJump = true;\n";
          }
          // Else, simply increase zkPC'=zkPC+1
          else if (!bFastMode)
          {
              code += "    pols.zkPC[nexti] = fr.add(pols.zkPC[i], fr.one());\n";
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
      • 136
      • 137
      • 138
      • 139
      • 140
      • 141
      • 142
      • 143

    JMP/CALL/JMPN/JMPC/RETURN 有条件跳转操作符 涉及的 列有:

    • 1)JMP:直接跳转,zkPC’=addr。
      pols.zkPC[nexti] = fr.fromU64(addr);
      pols.JMP[i] = fr.one();
      
      • 1
      • 2
    • 2)JMPC:若carry为1,则跳转,zkPC’=addr;否则,不跳转,只是zkPC’=zkPC+1。
      pols.JMPC[i] = fr.one();
      if (!fr.isZero(pols.carry[i]))
      {
          pols.zkPC[nexti] = fr.fromU64(addr); // If carry, jump to addr: zkPC'=addr
          bJump = true;
      }
      else
      {
              pols.zkPC[nexti] = fr.add(pols.zkPC[i], fr.one()); // If not carry, simply increase zkPC'=zkPC+1
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 3)JMPN:若op0<0,则跳转,zkPC’=addr;否则,不跳转,只是zkPC’=zkPC+1。
      pols.JMPN[i] = fr.one();
      fr.toS32(o, op0);
      if (o < 0) {
          pols.isNeg[i] = fr.one();
          pols.zkPC[nexti] = fr.fromU64(addr); // If op<0, jump to addr: zkPC'=addr
          required.Byte4[0x100000000 + o] = true;
          bJump = true;
      }
      else
      {
          pols.zkPC[nexti] = fr.add(pols.zkPC[i], fr.one()); // If op>=0, simply increase zkPC'=zkPC+1
          required.Byte4[o] = true;
          addr = 56;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • 4)ind:ind和indRR二者只能有一个为1,不能同时设置为1。ind是从E0中加载地址。若offset的值为整数,则实际地址为E0地址加1;若offset值为负数,则实际地址为E0地址加offset值。
      // If address is involved, load offset into addr
      fr.toS32(addrRel, pols.E0[i]);
      // If offset is possitive, and the sum is too big, fail
      if ((uint64_t(addrRel)+uint64_t(1))>=0x10000)
      {
          cerr << "Error: addrRel >= 0x10000 ln: " << 1415 << endl;
          exitProcess();
      }
      addrRel += 1;
      addr = addrRel;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 5)indRR:ind和indRR二者只能有一个为1,不能同时设置为1。indRR是从RR中加载地址。若offset的值为整数,则实际地址为E0地址加313;若offset值为负数,则实际地址为E0地址加offset值。
      // If address is involved, load offset into addr
      fr.toS32(addrRel, pols.RR[i]);
      // If offset is possitive, and the sum is too big, fail
      if ((uint64_t(addrRel)+uint64_t(313))>=0x10000)
      {
          cerr << "Error: addrRel >= 0x10000 ln: " << 1203 << endl;
          exitProcess();
      }
      addrRel += 313;
      addr = addrRel;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 6)offset:当ind和indRR均为0时,则实际地址为offset值:
      		else if (!bAddrRel && bOffset)
              {
                  if ((rom.program[zkPC].offset < 0) || (rom.program[zkPC].offset >= 0x10000))
                  {
                      console.log("Error: invalid offset=" + rom.program[zkPC].offset);
                      program.exit();
                  }
                  if (!bFastMode)
                      code += "    addrRel = " + rom.program[zkPC].offset + ";\n";
                  code += "    addr = " + rom.program[zkPC].offset + ";\n\n";
              }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 7)assignment:仅CALL操作符涉及。如assignment: { in: {type: ‘add’, values: [{type: ‘REG’, reg: ‘zkPC’}, {type: ‘CONST’, const: 1}] }, out:[‘RR’]},表示zkPC+1 => RR

    其中ind、indRR、offset之间对实际地址的影响关系,也可参见zkevm-proverjs sm_main_exec.js中的:

    		let addrRel = 0;
            let addr = 0;
            if (l.mOp || l.JMP || l.JMPN || l.JMPC ||  l.hashP || l.hashPLen || l.hashPDigest ||  l.hashK || l.hashKLen || l.hashKDigest || l.JMP || l.JMPC) {
                if (l.ind) {
                    addrRel = fe2n(Fr, ctx.E[0], ctx);
                }
                if (l.indRR) {
                    addrRel += fe2n(Fr, ctx.RR, ctx);
                }
                if (l.offset) addrRel += l.offset;
                if (addrRel >= 0x10000) throw new Error(`Address too big: ${ctx.ln} at ${ctx.fileName}:${ctx.line}`);
                if (addrRel <0 ) throw new Error(`Address can not be negative: ${ctx.ln} at ${ctx.fileName}:${ctx.line}`);
                addr = addrRel;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5. zkASM 地址运算

    zkASM相关地址运算addr有:【基于SP和PC等寄存器】

    地址运算对应列说明备注
    SP{
    $$ = { isStack: 1, isCode: 0, isMem:0, ind:0, indRR: 0, incCode:0, incStack:0, offset: 0, useCTX: 1}
    }
    SP ‘+’ NUMBER{
    $$ = { isStack: 1, isCode: 0, isMem:0, ind:0, indRR: 0, incCode:0, incStack: 0, offset: $3, useCTX: 1}}
    }
    SP ‘-’ NUMBER{
    $$ = { isStack: 1, isCode: 0, isMem:0, ind:0, indRR: 0, incCode:0, incStack: 0, offset: -$3, useCTX: 1}}
    }
    SP ‘++’{
    $$ = { isStack: 1, isCode: 0, isMem:0, ind:0, indRR: 0, incStack: 1, offset: 0, useCTX: 1}}
    }
    SP ‘–’{
    $$ = { isStack: 1, isCode: 0, isMem:0, ind:0, indRR: 0, incCode:0, incStack: -1, offset: 0, useCTX: 1}}
    }
    PC{
    $$ = { isStack: 0, isCode: 1, isMem:0, ind:0, indRR: 0, incCode:0, incStack: 0, offset: 0, useCTX: 1}
    }
    PC ‘+’ NUMBER{
    $$ = { isStack: 0, isCode: 1, isMem:0, ind:0, indRR: 0, incCode:0, incStack: 0, offset: $3, useCTX: 1}
    }
    PC ‘-’ NUMBER{
    $$ = { isStack: 0, isCode: 1, isMem:0, ind:0, indRR: 0, incCode:0, incStack: 0, offset: -$3, useCTX: 1}
    }
    PC ‘++’{
    $$ = { isStack: 0, isCode: 1, isMem:0, ind:0, indRR: 0, incCode:1, incStack: 0, offset: 0, useCTX: 1}
    }
    PC ‘–’{
    $$ = { isStack: 0, isCode: 1, isMem:0, ind:0, indRR: 0, incCode:-1, incStack: 0, offset: 0, useCTX: 1}
    }
    SYS ‘:’ E ‘+’ NUMBER{
    $$ = { isStack: 0, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: $5}
    }
    SYS ‘:’ E ‘-’ NUMBER{
    $$ = { isStack: 0, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: -$5}
    }
    SYS ‘:’ E{
    $$ = { isStack: 0, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: 0}
    }
    MEM ‘:’ E ‘+’ NUMBER{
    $$ = { isStack: 0, isMem: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: $5, useCTX: 1}
    }
    MEM ‘:’ E ‘-’ NUMBER{
    $$ = { isStack: 0, isMem: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: -$5, useCTX: 1}
    }
    MEM ‘:’ E{
    $$ = { isStack: 0, isMem: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: 0, useCTX: 1}
    }
    CODE ‘:’ E ‘+’ NUMBER{
    $$ = { isStack: 0, isCode: 1, ind:1, indRR: 0, incCode:0, incStack: 0, offset: $5, useCTX: 1}
    }
    CODE ‘:’ E ‘-’ NUMBER{
    $$ = { isStack: 0, isCode: 1, ind:1, indRR: 0, incCode:0, incStack: 0, offset: -$5, useCTX: 1}
    }
    CODE ‘:’ E{
    $$ = { isStack: 0, isCode: 1, ind:1, indRR: 0, incCode:0, incStack: 0, offset: 0, useCTX: 1}
    }
    STACK ‘:’ E ‘+’ NUMBER{
    $$ = { isStack: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: $5, useCTX: 1}
    }
    STACK ‘:’ E ‘-’ NUMBER{
    $$ = { isStack: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: -$5, useCTX: 1}
    }
    STACK ‘:’ E{
    $$ = { isStack: 1, isCode: 0, ind:1, indRR: 0, incCode:0, incStack: 0, offset: 0, useCTX: 1}
    }
    IDENTIFIER{
    $$ = { offset: $1 }
    }
    IDENTIFIER ‘+’ RR{
    $$ = { offset: $1, ind: 0, indRR: 1 }
    }
    IDENTIFIER ‘+’ E{
    $$ = { offset: $1, ind: 1, indRR: 0 }
    }

    6. zkEVM Storage Rom状态机中的zkASM语法

    zkEVM Storage Rom状态机中的zkASM语法,详细见:

    大多数语法与zkASM Compiler项目中的一致,本章仅罗列不同之处。

    6.1 Storage Rom中的zkASM寄存器

    Storage Rom中的zkASM寄存器reg有:

    寄存器名说明备注
    HASH_LEFT
    HASH_RIGHT
    OLD_ROOT
    NEW_ROOT
    VALUE_LOW
    VALUE_HIGH
    SIBLING_VALUE_HASH
    RKEY
    SIBLING_RKEY
    RKEY_BIT
    LEVEL
    PC
    ROTL_VH

    6.2 Storage Rom中的zkASM操作符

    Storage Rom中的zkASM操作符op有:

    操作符名对应列说明备注
    JMP ‘(’ IDENTIFIER ‘)’{
    $$ = {iJmp: 1n, addressLabel: $3}
    }
    JMPZ ‘(’ IDENTIFIER ‘)’{
    $$ = {iJmpz: 1n, addressLabel: $3}
    }
    有条件跳转。即JUMP Zero,是指当A寄存器的前一状态为0时,跳转到指定位置。
    HASH0{
    $$ = {iHash: 1n, iHashType: 0}
    }
    HASH1{
    $$ = {iHash: 1n, iHashType: 1}
    }
    LATCH_SET{
    $$ = {iLatchSet: 1n}
    }
    LATCH_GET{
    $$ = {iLatchGet: 1n}
    }
    CLIMB_RKEY{
    $$ = {iClimbRkey: 1n}
    }
    CLIMB_SIBLING_RKEY{
    $$ = { iClimbSiblingRkey: 1n}
    }
    CLIMB_SIBLING_RKEY_N{
    $$ = { iClimbSiblingRkeyN: 1n}
    }
    ROTATE_LEVEL{
    $$ = { iRotateLevel: 1n}
    }

    参考资料

    [1] zkASM Basic Syntax

    附录:Polygon Hermez 2.0 zkEVM系列博客

  • 相关阅读:
    IDEA调试并运行Spring源码
    windows下cmd命令行模式中cd变换路径命令无效的解决办法
    Linux代码初试__进度条
    【生活篇】~ “金九“前半段小结 及 晚来的中秋祝福
    gradle使用笔记整理
    uView实现全屏选项卡
    flowable
    互联网技术从业者怎么解决系统高并发?
    详解欧拉计划第188题:数的超幂
    @Transactional 注解导致跨库查询失效的问题
  • 原文地址:https://blog.csdn.net/mutourend/article/details/126838863