1. 引言
前序博客有:
- Polygon zkEVM 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(无效指令,没有意义):
zkASM通过${ExecutorMethod(params)} => A :JMP(param)
,即$和大括号来表示函数调用。
Polygon zkEVM在各状态机中实现了一系列的zkASM函数,本文重点关注如下代码库中的相关zkASM函数:
- 1)https://github.com/0xPolygonHermez/zkevm-proverjs
- 2)https://github.com/0xPolygonHermez/zkevm-rom
- 3)https://github.com/0xPolygonHermez/zkevm-storage-rom
zkevm-proverjs项目sm_main_exec.js文件中支持的函数调用有:
函数名函数实现说明beforeLastfunction eval_beforeLast(ctx) { if (ctx.step >= ctx.N-2) { return [0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]; } else { return [ctx.Fr.negone, 0n, 0n, 0n, 0n, 0n, 0n, 0n]; //不是倒数第二行,直接返回负值。 }}在主业务流程和最后清零操作之后,进行补零到execution trace表的倒数第二行。通常的调用方式类似为:【当beforeLast()返回负值时,会持续调用JMPN。】finalWait:
${beforeLast()} : JMPN(finalWait)
: JMP(start)
getGlobalHash本质上ctx.globalHahs=Keccak256(oldStateRoot|oldLocalExitRoot|newStateRoot|newLocalExitRoot|batchHashData|numBatch|timestamp),其中batchHashData=Keccak256(batchL2Data|globalExitRoot|sequencerAddr),这些参数均来自inputs-executor/*.json文件,均以16进制256bit表示,不足的前方补零。将ctx.globalHash值转换为8个32bit field elements表示。getOldStateRoot为inputs-executor/*.json文件中的oldStateRoot。将ctx.input.oldStateRoot值转换为8个32bit field elements表示。getNewStateRoot为inputs-executor/*.json文件中的newStateRoot。将ctx.input.newStateRoot值转换为8个32bit field elements表示。getSequencerAddr为inputs-executor/*.json文件中的sequencerAddr。将ctx.input.sequencerAddr值转换为8个32bit field elements表示。getOldLocalExitRoot为inputs-executor/*.json文件中的oldLocalExitRoot。将ctx.input.oldLocalExitRoot值转换为8个32bit field elements表示。getNewLocalExitRoot为inputs-executor/*.json文件中的newLocalExitRoot。将ctx.input.newLocalExitRoot值转换为8个32bit field elements表示。getNumBatch为inputs-executor/*.json文件中的numBatch。返回结果为[ctx.Fr.e(ctx.input.numBatch), ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero];
getTimestamp为inputs-executor/*.json文件中的timestamp。返回结果为[ctx.Fr.e(ctx.input.timestamp), ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero];
getBatchHashDatabatchHashData=Keccak256(batchL2Data|globalExitRoot|sequencerAddr),这些参数均来自inputs-executor/*.json文件,均以16进制256bit表示,不足的前方补零。将ctx.input.batchHashData值转换为8个32bit field elements表示。getGlobalExitRoot为inputs-executor/*.json文件中的globalExitRoot。将ctx.input.globalExitRoot值转换为8个32bit field elements表示。getTxsfunction eval_getTxs(ctx, tag) { if (tag.params.length != 2) throw new Error(“Invalid number of parameters function …”); const txs = ctx.input.batchL2Data; const offset = Number(evalCommand(ctx,tag.params[0])); const len = Number(evalCommand(ctx,tag.params[1])); let d = “0x” + txs.slice(2+offset*2, 2+offset*2 + len*2); if (d.length == 2) d = d+‘0’; return scalar2fea(ctx.Fr, Scalar.e(d)); }从ctx.input.batchL2Data中截取特定长度值,转换为8个32bit field elements表示。getTxsLen为:(ctx.input.batchL2Data.length-2) / 2返回的结果为[ctx.Fr.e((ctx.input.batchL2Data.length-2) / 2), ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero]
eventLog返回为全零值:[ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero, ctx.Fr.zero];
cond有条件返回,若evalCommand结果为true,则返回-1;否则返回0。inverseFpEc输入为a,若a为0,抛异常;否则返回
a
−
1
a^{-1}
a−1inverseFnEc输入为a,若a为0,抛异常;否则返回
a
−
1
a^{-1}
a−1sqrtFpEc输入为a,返回
a
\sqrt{a}
a
dumpRegs打印A/B/C/D/E寄存器中的值,并返回0。dump打印输入信息,并返回0。dumphex以16进制打印输入信息,并返回0。xAddPointEc取不同点求和后的x坐标。yAddPointEc取不同点求和后的y坐标。xDblPointEc取相同点求和后的x坐标。yDblPointEc取相同点求和后的y坐标。test**对应test_tools.js中的相关函数getBytecodeevalCommand获得hashContract=》从ctx.input.contractsBytecode中获得bytecode,从bytecode中截取特定长度值,转换为8个32bit field elements表示。touchedAddress若地址对应为某预编译合约,则考虑warm access,直接返回0。若该地址在ctx.input.touchedAddress数组之中,则返回0;否则将addr放入ctx.input.touchedAddress中并返回1。touchedStorageSlots输入为addr和key,若addr已在ctx.input.touchedStorageSlots中,则返回0;否则将{addr,key}存入ctx.input.touchedStorageSlots中,并返回1。**bitwise**输入为a,b,根据具体的函数名进行add/or/xor/not运算。**comp**输入为a,b,根据具体的函数名进行lt/gt/eq运算。loadScalar读取相应参数中的值并处理log输入为frLog和label,打印frLog等信息。返回0。resetTouchedAddress将ctx.input.touchedAddress置空,返回0。resetStorageSlots将ctx.input.touchedStorageSlots置空,并返回0。exp输入为a,b,将
a
b
a^b
ab结果转换为8个32bit field elements表示。storeLog输入为indexLog、isTopic、data,若ctx.outLogs[indexLog]未定义,若isTopic为true,则将data以十六进制形式存入ctx.outLogs[indexLog].topics中;否则将data以十六进制形式存入ctx.outLogs[indexLog].data中。返回0。**precompiled**break打印断点信息,返回0。memAlignWR_W0输入为m0,value,offset,进行处理后将结果以8个32bit field elements表示。memAlignWR_W1输入为m1,value,offset,进行处理后将结果以8个32bit field elements表示。memAlignWR8_W0输入为m0,value,offset,取bits=(31-offset)*8,进行处理后将结果以8个32bit field elements表示。saveContractBytecode输入为addr,将ctx.hashP[addr].data 存入 ctx.input.contractsBytecode[ctx.hashP[addr].digest] 中,并返回0。
3. zkevm-proverjs项目sm_storage.js文件中的函数
zkevm-proverjs项目sm_storage.js文件中的函数调用有:【对应为Storage状态机的action。Storage状态机的输入和输出状态实际上是SMT(Sparse Merkle Tree),所以相应的action也是SMT set】
函数名函数实现说明isSetUpdate若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "update")
为true,则设置op[0]=1。update existing valueisSetInsertFound若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "insertFound")
为true,则设置op[0]=1。insert with found key; found a leaf node with a common set of key bitsisSetInsertNotFound若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "insertNotFound")
为true,则设置op[0]=1。insert with no found keyisSetReplacingZero若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "insertNotFound")
为true,则设置op[0]=1。替换为0isSetDeleteLast若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteLast")
为true,则设置op[0]=1。delete the last node, so root becomes 0isSetDeleteFound若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteFound")
为true,则设置op[0]=1。delete with found keyisSetDeleteNotFound若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteNotFound")
为true,则设置op[0]=1。delete with no found keyisSetZeroToZero若(!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "zeroToZero")
为true,则设置op[0]=1。value was zero and remains zeroGetIsOld0若!actionListEmpty && (action[a].bIsSet ? action[a].setResult.isOld0 : action[a].getResult.isOld0)
为true,则设置op[0]=1。can be a final leaf (isOld0=true)。isGet若(!actionListEmpty && !action[a].bIsSet)
为true,则设置op[0]=1。若key not found,则返回0;否则,返回非零值。GetRkey设置op[0/1/2/3]=ctx.rkey[0/1/2/3]。Get the remaining key, i.e. the key after removing the bits used in the tree node navigation。GetSiblingRkey设置op[0/1/2/3]=ctx.rkey[0/1/2/3]。Get the sibling remaining key, i.e. the part that is not common to the value key。GetSiblingHash若action[a].bIsSet为true对应setResult,否则为getResult,相应设置op[0/1/2/3]=action[a].set/getResult.siblings[ctx.currentLevel][(1n-ctx.bits[ctx.currentLevel])*4n+0/1/2/3n]
。Get the sibling hash, obtained from the siblings array of the current level, taking into account that the sibling bit is the opposite (1-x) of the value bit。GetValueLow取action的getResult.value或setResult.newValue值,将该值的lower 4 elements赋值为op[0/1/2/3]。Value is an u256 split in 8 u32 chuncks, each one stored in the lower 32 bits of an u63 field element。u63 means that it is not an u64, since some of the possible values are lost due to the prime effect。GetValueHigh取action的getResult.value或setResult.newValue值,将该值的higher 4 elements赋值为op[0/1/2/3]。GetSiblingValueLow取action的getResult.insValue或setResult.insValue值,将该值的lower 4 elements赋值为op[0/1/2/3]。GetSiblingValueHigh取action的getResult.insValue或setResult.insValue值,将该值的higher 4 elements赋值为op[0/1/2/3]。GetOldValueLow取action的setResult.oldValue值,将该值的lower 4 elements赋值为op[0/1/2/3]。GetOldValueHigh取action的setResult.oldValue值,将该值的higher 4 elements赋值为op[0/1/2/3]。GetLevelBit输入为单个bit,若( ctx.level & (1
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?