前序博客有:
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(无效指令,没有意义):
以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:
其经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
}
}
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 。 |
zkASM语言支持2种scope
:
scope标志 | 说明 | 备注 |
---|---|---|
GLOBAL | ||
CTX |
[a-zA-Z_][a-zA-Z$_0-9]*
,表示由字母和数字组成。function setLine(dst, first) {
dst.line = first.first_line;
}
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 $$; } |
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) } |
zkASM语言支持的statement label
类型有:
label标志 | 说明 | 备注 |
---|---|---|
IDENTIFIER ‘:’ | {$$ = {type: "label", identifier: $1}; setLine($$, @1) } |
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 } } |
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 } |
zkASM语言支持的statement include
类型有:
include标志 | 说明 | 备注 |
---|---|---|
INCLUDE STRING | { $$ = {type: “include”, file: $2} } |
zkASM语言支持的statement command
类型有:
command标志 | 说明 | 备注 |
---|---|---|
COMMAND | { $$ = {type: “command”, cmd: $1} } |
zkASM语言command中支持的leftExpression
有:
leftExpression标志 | 说明 | 备注 |
---|---|---|
VAR IDENTIFIER | { $$ = {op: “declareVar”, varName: $2} } |
|
IDENTIFIER | { $$ = {op: “getVar”, varName: $1} } |
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} } |
zkASM语言command中支持的e1
有:
e1标志 | 说明 | 备注 |
---|---|---|
functionCall | { $$ = $1; } |
|
e0 | { $$ = $1 } |
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; } |
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; } |
zkASM语言command中支持的e4
有:
e4标志 | 说明 | 备注 |
---|---|---|
e4 ‘+’ e3 | { $$ = { op: “add”, values: [$1, $3] }; } |
|
e4 ‘-’ e3 | { $$ = { op: “sub”, values: [$1, $3] }; } |
|
e3 %prec EMPTY | { $$ = $1; } |
zkASM语言command中支持的e5
有:
e5标志 | 说明 | 备注 |
---|---|---|
leftExpression ‘=’ e5 | { $$ = { op: “setVar”, values: [$1, $3] }; } |
|
e4 %prec EMPTY | { $$ = $1; } |
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: []} } |
zkASM语言支持的寄存器reg
有:
寄存器名 | 说明 | 备注 |
---|---|---|
A | ASSERT操作符仅作用于A寄存器 | 如B : ASSERT ,表示断言A寄存器中的值与B寄存器中的值相等 |
B | ||
C | ||
D | ||
E | ||
SR | Storage Merkle tree State Root寄存器 | SLOAD和SSTORE会对该寄存器代表的sparse merkle tree进行读写。 |
CTX | ||
SP | Stack pointer | |
PC | Program counter | 为实现有条件跳转,引入PC(Program Counter),记录了程序执行的当前指令的位置。 |
GAS | ||
RR | 与zkPC寄存器配合使用,控制子程序跳转和返回。如zkPC+1 => RR :JMP(subroutine) ; 等效为CALL(subroutime) ;:RETURN ; 等效为 :JMP(RR) |
|
zkPC | Zero-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] } |
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} } |
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 x1⋅y1+x2−y2⋅2256−y3=0。实际为 A ⋅ B + C = D ⋅ 2 256 + E A\cdot B+C=D\cdot 2^{256}+E A⋅B+C=D⋅2256+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.js
中execute
函数中的incCounter
具体取值为:
incCounter = Math.ceil((ctx.hashP[addr].data.length + 1) / 56);
incCounter = Math.ceil((ctx.hashK[addr].data.length + 1) / 136)
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)),
};
}
zkASM操作符列表opList
表示为:
操作符列表标志 | 说明 | 备注 |
---|---|---|
opList ‘,’ op | { $1.push($3); $$ = $1 } |
|
op | { $$ = [$1] } |
Polygon zkEVM Poseidon哈希运算相关操作符和寄存器有:
setHASHPOS
常量多项式。hashP、ind、indRR、offset
常量多项式。hashPLen、ind、indRR、offset
常量多项式。hashPDigest、ind、indRR、offset
常量多项式。这些多项式之间的逻辑关系见zkevm-proverjs/src/sm/sm_main/sm_main_exec.js
中的execute
函数:
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);
}
以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
具体参看:
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";
}
JMP/CALL/JMPN/JMPC/RETURN 有条件跳转操作符 涉及的 列有:
pols.zkPC[nexti] = fr.fromU64(addr);
pols.JMP[i] = fr.one();
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
}
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;
}
// 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;
// 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;
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";
}
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;
}
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 } } |
zkEVM Storage Rom状态机中的zkASM语法,详细见:
大多数语法与zkASM Compiler项目中的一致,本章仅罗列不同之处。
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 |
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