Polygon zkEVM zkASM中的函数集合

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

2. zkevm-proverjs项目sm_main_exec.js文件中的函数

zkevm-proverjs项目sm_main_exec.js文件中支持的函数调用有:

函数名 函数实现 说明
beforeLast function 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];
getBatchHashData batchHashData=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表示。
getTxs function 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} a1
inverseFnEc 输入为a,若a为0,抛异常;否则返回 a − 1 a^{-1} a1
sqrtFpEc 输入为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中的相关函数
getBytecode evalCommand获得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 value
isSetInsertFound (!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 bits
isSetInsertNotFound (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "insertNotFound")为true,则设置op[0]=1。insert with no found key
isSetReplacingZero (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "insertNotFound")为true,则设置op[0]=1。替换为0
isSetDeleteLast (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteLast")为true,则设置op[0]=1。delete the last node, so root becomes 0
isSetDeleteFound (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteFound")为true,则设置op[0]=1。delete with found key
isSetDeleteNotFound (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "deleteNotFound")为true,则设置op[0]=1。delete with no found key
isSetZeroToZero (!actionListEmpty && action[a].bIsSet && action[a].setResult.mode == "zeroToZero")为true,则设置op[0]=1。value was zero and remains zero
GetIsOld0 !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<为true,则设置op[0]=1。
GetTopTree 仅当到达the top of the tree(即ctx.currentLevel=0),返回0。
GetTopOfBranch Returns 0 if we reached the top of the branch, i.e. if the level matches the siblings size。
GetNextKeyBit Get the next key bit。该调用会自动减少the current level。
isAlmostEndPolynomial if (i == (polSize-2))
{
  op[0] = fr.one;
}
与前面的beforeLast函数功能类似。在主业务流程和最后清零操作之后,进行补零到execution trace表的倒数第二行。通常的调用方式类似为:【仅当为倒数第二行时,isAlmostEndPolynomial才返回1,否则返回均为0值。若返回0值,则持续调用NotEndPol。】
NotEndPol:
  ${isAlmostEndPolynomial()} :JMPZ(NotEndPol)
                                                            :JMP(Run) ;为execution trace的最后一行。

附录:Polygon Hermez 2.0 zkEVM系列博客

  • ZK-Rollups工作原理
  • Polygon zkEVM——Hermez 2.0简介
  • Polygon zkEVM网络节点
  • Polygon zkEVM 基本概念
  • Polygon zkEVM Prover
  • Polygon zkEVM工具——PIL和CIRCOM
  • Polygon zkEVM节点代码解析
  • Polygon zkEVM的pil-stark Fibonacci状态机初体验
  • Polygon zkEVM的pil-stark Fibonacci状态机代码解析
  • Polygon zkEVM PIL编译器——pilcom 代码解析
  • Polygon zkEVM Arithmetic状态机
  • Polygon zkEVM中的常量多项式
  • Polygon zkEVM Binary状态机
  • Polygon zkEVM Memory状态机
  • Polygon zkEVM Memory Align状态机
  • Polygon zkEVM zkASM编译器——zkasmcom
  • Polygon zkEVM哈希状态机——Keccak-256和Poseidon
  • Polygon zkEVM zkASM语法
  • Polygon zkEVM可验证计算简单状态机示例
  • Polygon zkEVM zkASM 与 以太坊虚拟机opcode 对应集合

你可能感兴趣的:(zkVM,零知识证明)