appliedzkp的zkevm(5)State Circuit

1. 引言

State circuit的作用是,作为:random accessible data holder of stack, memory, storage, and all the other things evm interpreter could access at any time。

State circuit可分为3个子circuit:

  • stack sub-circuit
  • memory sub-circuit
  • storage sub-circuit

以如下solidity代码为例:

pragma solidity ^0.8;

contract Sample {
    function memory_sample() public pure {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0xdeadbeaf)
            mstore(ptr, add(mload(ptr), 0xfaceb00c))
            mstore(add(ptr, 0x20), 0xcafeb0ba)
        }
    }
}

当执行memory_sample函数时,evm中会有如下log:

pc  op              stack (top -> down)                  memory
--  --------------  ----------------------------------   ---------------------------------------  
... 
53  JUMPDEST        [    ,          ,           ,    ]   {40: 80,  80:          ,  a0:         }
54  PUSH1 40        [    ,          ,           ,  40]   {40: 80,  80:          ,  a0:         }
56  MLOAD           [    ,          ,           ,  80]   {40: 80,  80:          ,  a0:         }
57  PUSH4 deadbeaf  [    ,          ,   deadbeef,  80]   {40: 80,  80:          ,  a0:         }
62  DUP2            [    ,        80,   deadbeef,  80]   {40: 80,  80:          ,  a0:         }
63  MSTORE          [    ,          ,           ,  80]   {40: 80,  80:  deadbeef,  a0:         }
64  PUSH4 faceb00c  [    ,          ,   faceb00c,  80]   {40: 80,  80:  deadbeef,  a0:         }
69  DUP2            [    ,        80,   faceb00c,  80]   {40: 80,  80:  deadbeef,  a0:         }
70  MLOAD           [    ,  deadbeef,   faceb00c,  80]   {40: 80,  80:  deadbeef,  a0:         }
71  ADD             [    ,          ,  1d97c6efb,  80]   {40: 80,  80:  deadbeef,  a0:         }
72  DUP2            [    ,        80,  1d97c6efb,  80]   {40: 80,  80:  deadbeef,  a0:         }
73  MSTORE          [    ,          ,           ,  80]   {40: 80,  80: 1d97c6efb,  a0:         }
74  PUSH4 cafeb0ba  [    ,          ,   cafeb0ba,  80]   {40: 80,  80: 1d97c6efb,  a0:         }
79  PUSH1 20        [    ,        20,   cafeb0ba,  80]   {40: 80,  80: 1d97c6efb,  a0:         }
81  DUP3            [  80,        20,   cafeb0ba,  80]   {40: 80,  80: 1d97c6efb,  a0:         }
82  ADD             [    ,        a0,   cafeb0ba,  80]   {40: 80,  80: 1d97c6efb,  a0:         }
83  MSTORE          [    ,          ,           ,  80]   {40: 80,  80: 1d97c6efb,  a0: cafeb0ba}
84  POP             [    ,          ,           ,    ]   {40: 80,  80: 1d97c6efb,  a0: cafeb0ba}
...

相关定义有:

  • fq:253-bit value
  • op:表示EVM operation code
  • pc:Program counter
  • gc:Global counter,简化为offset to 0
  • sp:Stack pointer
  • stack:为fq 向量,max size为1024
  • memory:为a vector of bytes
  • $a == $b:表示 $a 等于 $b
  • $t_lookup:为用于保证input在 $t 表中的函数

2. memory circuit

在memory circuit中,Prover应收集所有的MLOADMSTORE操作,然后根据keygc排序,然后构建a layout with column key, val, rwgc,分别表示:

  • key:为 key of memory we are operating
  • val:为操作后的 memory[key] 值
  • rw:为access enum,如0表示Read,1表示Write

辅助的信息有:

  • key_prev:为前一行的key value
  • gc_prev:为前一行的gc value
  • val_prev:为前一行的 val value

memory circuit中的constraint为:
在这里插入图片描述
以上solidity例子对应的memory table为:【根据 以太坊黄皮书 可知,MLOAD对应为1 memory read + 1 stack read + 1 stack write。DUP 对应为1 stack read + 1 stack write。MSTORE对应2 stack read + 1 memory write。PUSH对应 1 stack write。SWAP对应 2 stack read + 2 stack write。】
在这里插入图片描述

3. Stack Circuit

Stack circuit与memory circuit类似,但是为了防止在PUSH或POP时对每个entry的修改,设置在evm circuit中维护一个stack pointer sp,sp的初始位置为1024 to look up the top value of stack。如,PUSH时 s p − − sp-- sp,POP时 s p + + sp++ sp++
相应的Stack circuit constraint为:
在这里插入图片描述
某些op可同时对应 多个stack read/write,如DUPX,此时需check if the source and new pushed value is equal,因此要求 a Read and a Write to be in bus mapping。

设置evm circuit中使用multiple lookup来保证这些read/write是同时发生的(对于memory也一样),因此需有如下constraints:(其中 $x 表示变量,应与相同的 op 一致。)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上solidity例子对应的stack table为:
在这里插入图片描述
在这里插入图片描述

4. Storage circuit

在Storage circuit中,需考虑在sub call中遇到REVERT时的各种应对策略。此处,主要关注:

  • storage of account state
  • error caused by REVERT

相关定义有:

  • gc:global counter
  • addr:account address holding opcodes
  • pc:program counter
  • op:operator respect to addr and pc
  • sp:stack pointer
  • is_persistant:为a binary flag that tells if this call succeeds in the future (immutable with a call)
  • gc_end_of_call:a number for us to know when the call ends, we use this to rollback storage write in FILO order (immutable within a call)
  • sstore_counter:为a counter to count how many SSTORE we have done。

在Storage circuit中,有类似evm的层级namespace——group access records by account address and then storage location,然后在每个sub-groups (each location of an account), 按global counter递增对records排序。

(不同于stack和memory,stack和memory可seperated properly by some other unique identical call context instead of only account address for indicating the source of CALLDATA and RETURNDATA。)

同时,为了便于rollback,当 Storage Write时,在access record中额外增加了val_prev。
根据 EIP2929,在bus mapping中额外引入了is_first_touch,当a storage location is touched for the first time in a EOA(externally owned account) call时,该值应设置为1。

最终的bus mapping lookup为:

bus_mapping_lookup(
    gc,
    Storage,
    key,
    val,
    rw,
    val_prev,
    is_first_touch,
)

4.1 Multi-Call Example

object "Caller" {
    code {
        datacopy(0, dataoffset("Callee"), datasize("Callee"))
        let callee := create(0, 0, datasize("Callee"))

        datacopy(0, dataoffset("runtime"), datasize("runtime"))
        setimmutable(0, "callee", callee)
        return(0, datasize("runtime"))
    }

    object "runtime" {
        code {
            let callee := loadimmutable("callee")

            // call 1: success
            mstore(0, 1)
            pop(call(30000, callee, 0, 0, 32, 0, 0))

            // call 2: revert
            mstore(0, 0)
            pop(call(30000, callee, 0, 0, 32, 0, 0))

            // call 3: success
            mstore(0, 1)
            pop(call(30000, callee, 0, 0, 32, 0, 0))
        }
    }

    object "Callee" {
        code {
            datacopy(0, dataoffset("runtime"), datasize("runtime"))
            return(0, datasize("runtime"))
        }

        object "runtime" {
            code {
                // counter++
                sstore(0, add(sload(0), 1))

                // counter++
                sstore(0, add(sload(0), 1))

                if calldataload(0) { stop() }
                revert(0, 0)
            }
        }
    }
}

在上面的例子中:

  • Caller 触发 Callee 做3次简单的storage操作—— Callee.0++,但是其中第2次操作将revert。

calling Caller 的executation trace如下:【为简单起见,将其中的所有DUP替换为了PUSH, ID替换为了CALLIndex,RV替换为了Revert。】

ID   RV   PC    OP             STACK                          STORAGE   
---- ---- ----- -------------- ------------------------------ ---------- 
  0    0     0   PUSH1 1        [     ,  ,  ,  ,   ,   , 1 ]             
  0    0     2   PUSH1 0        [     ,  ,  ,  ,   ,  0, 1 ]             
  0    0     4   MSTORE         [     ,  ,  ,  ,   ,   ,   ]             
  0    0     5   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]             
  0    0     7   PUSH1 0        [     ,  ,  ,  ,   ,  0, 0 ]             
  0    0     9   PUSH1 20       [     ,  ,  ,  , 20,  0, 0 ]             
  0    0    11   PUSH1 0        [     ,  ,  , 0, 20,  0, 0 ]             
  0    0    13   PUSH1 0        [     ,  , 0, 0, 20,  0, 0 ]             
  0    0    15   PUSH20 a       [     , a, 0, 0, 20,  0, 0 ]             
  0    0    36   PUSH2 c350     [ c350, a, 0, 0, 20,  0, 0 ]             
  0    0    39   CALL           [     ,  ,  ,  ,   ,   ,   ]             
  1    0     0   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 0 }  
  1    0     2   SLOAD          [     ,  ,  ,  ,   ,   , 0 ]   { 0: 0 }  
  1    0     3   PUSH1 1        [     ,  ,  ,  ,   ,  1, 0 ]   { 0: 0 }  
  1    0     5   ADD            [     ,  ,  ,  ,   ,   , 1 ]   { 0: 0 }  
  1    0     6   PUSH1 0        [     ,  ,  ,  ,   ,  0, 1 ]   { 0: 0 }  
  1    0     8   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 1 }  
  1    0     9   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 1 }  
  1    0    11   SLOAD          [     ,  ,  ,  ,   ,   , 1 ]   { 0: 1 }  
  1    0    12   PUSH1 1        [     ,  ,  ,  ,   ,  1, 1 ]   { 0: 1 }  
  1    0    14   ADD            [     ,  ,  ,  ,   ,   , 2 ]   { 0: 1 }  
  1    0    15   PUSH1 0        [     ,  ,  ,  ,   ,  0, 2 ]   { 0: 1 }  
  1    0    17   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 2 }  
  1    0    18   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 2 }  
  1    0    20   CALLDATALOAD   [     ,  ,  ,  ,   ,   , 1 ]   { 0: 2 }  
  1    0    21   ISZERO         [     ,  ,  ,  ,   ,   , 0 ]   { 0: 2 }  
  1    0    22   PUSH2 1b       [     ,  ,  ,  ,   , 1b, 0 ]   { 0: 2 }  
  1    0    25   JUMPI          [     ,  ,  ,  ,   ,   ,   ]   { 0: 2 }  
  1    0    26   STOP           [     ,  ,  ,  ,   ,   , 1 ]   { 0: 2 }  
  0    0    40   POP            [     ,  ,  ,  ,   ,   ,   ]             
  0    0    41   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]             
  0    0    43   PUSH1 0        [     ,  ,  ,  ,   ,  0, 0 ]             
  0    0    45   MSTORE         [     ,  ,  ,  ,   ,   ,   ]             
  0    0    46   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]             
  0    0    48   PUSH1 0        [     ,  ,  ,  ,   ,  0, 0 ]             
  0    0    50   PUSH1 20       [     ,  ,  ,  , 20,  0, 0 ]             
  0    0    52   PUSH1 0        [     ,  ,  , 0, 20,  0, 0 ]             
  0    0    54   PUSH1 0        [     ,  , 0, 0, 20,  0, 0 ]             
  0    0    56   PUSH20 a       [     , a, 0, 0, 20,  0, 0 ]             
  0    0    77   PUSH2 c350     [ c350, a, 0, 0, 20,  0, 0 ]             
  0    0    80   CALL           [     ,  ,  ,  ,   ,   ,   ]             
  2    1     0   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 2 }  
  2    1     2   SLOAD          [     ,  ,  ,  ,   ,   , 2 ]   { 0: 2 }  
  2    1     3   PUSH1 1        [     ,  ,  ,  ,   ,  1, 2 ]   { 0: 2 }  
  2    1     5   ADD            [     ,  ,  ,  ,   ,   , 3 ]   { 0: 2 }  
  2    1     6   PUSH1 0        [     ,  ,  ,  ,   ,  0, 3 ]   { 0: 2 }  
  2    1     8   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 3 }  
  2    1     9   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 3 }  
  2    1    11   SLOAD          [     ,  ,  ,  ,   ,   , 3 ]   { 0: 3 }  
  2    1    12   PUSH1 1        [     ,  ,  ,  ,   ,  1, 3 ]   { 0: 3 }  
  2    1    14   ADD            [     ,  ,  ,  ,   ,   , 4 ]   { 0: 3 }  
  2    1    15   PUSH1 0        [     ,  ,  ,  ,   ,  0, 4 ]   { 0: 3 }  
  2    1    17   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 4 }  
  2    1    18   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 4 }  
  2    1    20   CALLDATALOAD   [     ,  ,  ,  ,   ,   , 0 ]   { 0: 4 }  
  2    1    21   ISZERO         [     ,  ,  ,  ,   ,   , 1 ]   { 0: 4 }  
  2    1    22   PUSH2 1b       [     ,  ,  ,  ,   , 1b, 1 ]   { 0: 4 }  
  2    1    25   JUMPI          [     ,  ,  ,  ,   ,   ,   ]   { 0: 4 }  
  2    1    27   JUMPDEST       [     ,  ,  ,  ,   ,   ,   ]   { 0: 4 }  
  2    1    28   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 4 }  
  2    1    30   PUSH1 0        [     ,  ,  ,  ,   ,  0, 0 ]   { 0: 4 }  
  2    1    32   REVERT         [     ,  ,  ,  ,   ,   , 0 ]   { 0: 2 }  
  0    0    81   POP            [     ,  ,  ,  ,   ,   ,   ]             
  0    0    82   PUSH1 1        [     ,  ,  ,  ,   ,   , 1 ]             
  0    0    84   PUSH1 0        [     ,  ,  ,  ,   ,  0, 1 ]             
  0    0    86   MSTORE         [     ,  ,  ,  ,   ,   ,   ]             
  0    0    87   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]             
  0    0    89   PUSH1 0        [     ,  ,  ,  ,   ,  0, 0 ]             
  0    0    91   PUSH1 20       [     ,  ,  ,  , 20,  0, 0 ]             
  0    0    93   PUSH1 0        [     ,  ,  , 0, 20,  0, 0 ]             
  0    0    95   PUSH1 0        [     ,  , 0, 0, 20,  0, 0 ]             
  0    0    97   PUSH20 a       [     , a, 0, 0, 20,  0, 0 ]             
  0    0   118   PUSH2 c350     [ c350, a, 0, 0, 20,  0, 0 ]             
  0    0   121   CALL           [     ,  ,  ,  ,   ,   ,   ]             
  3    0     0   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 2 }  
  3    0     2   SLOAD          [     ,  ,  ,  ,   ,   , 2 ]   { 0: 2 }  
  3    0     3   PUSH1 1        [     ,  ,  ,  ,   ,  1, 2 ]   { 0: 2 }  
  3    0     5   ADD            [     ,  ,  ,  ,   ,   , 3 ]   { 0: 2 }  
  3    0     6   PUSH1 0        [     ,  ,  ,  ,   ,  0, 3 ]   { 0: 2 }  
  3    0     8   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 3 }  
  3    0     9   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 3 }  
  3    0    11   SLOAD          [     ,  ,  ,  ,   ,   , 3 ]   { 0: 3 }  
  3    0    12   PUSH1 1        [     ,  ,  ,  ,   ,  1, 3 ]   { 0: 3 }  
  3    0    14   ADD            [     ,  ,  ,  ,   ,   , 4 ]   { 0: 3 }  
  3    0    15   PUSH1 0        [     ,  ,  ,  ,   ,  0, 4 ]   { 0: 3 }  
  3    0    17   SSTORE         [     ,  ,  ,  ,   ,   ,   ]   { 0: 4 }  
  3    0    18   PUSH1 0        [     ,  ,  ,  ,   ,   , 0 ]   { 0: 4 }  
  3    0    20   CALLDATALOAD   [     ,  ,  ,  ,   ,   , 1 ]   { 0: 4 }  
  3    0    21   ISZERO         [     ,  ,  ,  ,   ,   , 0 ]   { 0: 4 }  
  3    0    22   PUSH2 1b       [     ,  ,  ,  ,   , 1b, 0 ]   { 0: 4 }  
  3    0    25   JUMPI          [     ,  ,  ,  ,   ,   ,   ]   { 0: 4 }  
  3    0    26   STOP           [     ,  ,  ,  ,   ,   , 1 ]   { 0: 4 }  
  0    0   122   POP            [     ,  ,  ,  ,   ,   ,   ]             
  0    0   123   STOP           [     ,  ,  ,  ,   ,   ,   ]

对应的storage circuit witnesses为:【简单起见,忽略is_first_touch,因为其仅在第一行为1。】
在这里插入图片描述
可发现,在ID=2时,the storage writes are undone in a FILO (first in last out) order。在evm circuit中,将constraint the number of SSTORE and SSTORE.REVERT happens together and in FILO order when is_persistant = 0, which ensures all storage writes to be undone correctly。

evm circuit中的SSTORE伪代码类似为:

// read location and value 
bus_mapping_lookup(gc, Stack, sp, loc, Read)
bus_mapping_lookup(gc+1, Stack, sp+1, val, Read)

// write storage
bus_mapping_lookup(gc+2, Storage, loc, val, Write, val_prev, is_first_touch)
    
if !is_persistant {
    // rollback storage in a "first in last out" order
    bus_mapping_lookup(
        gc_end_of_call - sstore_counter,
        Storage,
        loc,
        val_prev,
        Write,
        val,
        is_first_touch,
    )
}

// sstore_counter should increase 1
sstore_counter_next === sstore_counter + 1

gc_next === gc + 3 // gc should increase 3
pc_next === pc + 1 // pc should increase 1
sp_next === sp + 2 // sp should increase 2 (SSTORE = 2 POP)

evm circuit中的RETURN和REVERT伪代码类似为:

// read offset and size (of return data)
bus_mapping_lookup(gc, Stack, sp, offset, Read)
bus_mapping_lookup(gc+1, Stack, sp+1, size, Read)

// TODO: check offset and size are set in parent's context for RETURNDATA

// should be persistant if RETURN
if op == RETURN {
    is_persistant === 1
}

// should not be persistant if REVERT
if op == REVERT {
    is_persistant === 0
}

// check no extra records within revert section
gc_end_of_call === gc + 1 + sstore_counter

// gc should jump to correct one
gc_next === gc_end_of_call + 1

// TODO: pc_next should set back to parent's next one
// TODO: sp_next should set back to parent's next one

4.2 Smaller but Complete Example

若有如下trace:

 PC   OP        STACK      STORAGE         
---- --------- ---------- ---------------- 
  0   PUSH1 1   [  , 1 ]   { 6: 0, a: 0 }  
  2   PUSH1 a   [ a, 1 ]   { 6: 0, a: 0 }  
  4   SSTORE    [  ,   ]   { 6: 0, a: 1 }  
  5   PUSH1 3   [  , 3 ]   { 6: 0, a: 1 }  
  7   PUSH1 6   [ 6, 3 ]   { 6: 0, a: 1 }  
  9   SSTORE    [  ,   ]   { 6: 3, a: 1 }  
 10   PUSH1 0   [  , 0 ]   { 6: 3, a: 1 }  
 12   PUSH1 0   [ 0, 0 ]   { 6: 3, a: 1 }  
 14   REVERT    [ 0, 0 ]   { 6: 0, a: 0 } 

则根据gc增序排列的bus mapping table为:【为简单起见,忽略了is_first_touch,其仅在 gc=5 和 gc=10 的行对应值才为1。】
在这里插入图片描述
相关context为:

  • gc_end_of_call=16
  • is_persistant=0

上图中,绿色的行对应为相同的 4 SSTORE,具体为:

bus_mapping_lookup(5, Storage, a, 1, Write, 0, 1)

// gc = gc_end_of_call - sstore_counter = 16 - 0 = 16
bus_mapping_lookup(16, Storage, a, 0, Write, 1, 0)

sstore_counter_next === sstore_counter + 1 // sstore_counter_next = 0 + 1 = 1

橙色的行对应为相同的 9 SSTORE,具体为:

bus_mapping_lookup(10, Storage, 6, 3, Write, 0, 1)

// gc = gc_end_of_call - sstore_counter = 16 - 1 = 15
bus_mapping_lookup(15, Storage, 6, 0, Write, 3, 0)

sstore_counter_next === sstore_counter + 1 // sstore_counter_next = 1 + 1 = 2

当REVERT时,至少需做如下check:

// no extra records within revert section
gc_end_of_call === gc + 1 + sstore_counter // 16 = 13 + 1 + 2

// next gc jump to after gc_end_of_call
gc_next === gc_end_of_call + 1

4.3 Update Storage Root on L1 Contract

For all identical locations of address:【注意其中的build 和 open 动作为设计circuit中的merkle proof verification。】

  • the init rows will be attached with a merkle proof to ensure it’s read from old state root.
  • the first one in total will be verified with the old state root as a public input from L1 contract.
  • the last row of each location builds a intermediate state root using the same merkle proof of its init row with its final value.
  • next location opens the intermediate state root to read its old value. 原因在于we know different locations will no be updated due to the location group constraint.
  • in the end of storage circuit, 可build the finalized state root. 然后use a public input to verify the equality of the result, and update it to L1 contract。

4.4 QA

Q1: How to handle other world state update?
会引起state tire update的操作有:

  • 1)Transaction—— nonce 和 balance 将更新
  • 2)CALL——若CALL具有non-zero value,则balance 将更新
  • 3)CREATE, CREATE2——hash of code将更新到新的合约地址
  • 4)SSTORE——上文已提及。

Q2: How to handle errors caused not by REVERT?

// List evm execution errors
var (
	ErrOutOfGas                 = errors.New("out of gas")
	ErrCodeStoreOutOfGas        = errors.New("contract creation code storage out of gas")
	ErrDepth                    = errors.New("max call depth exceeded")
	ErrInsufficientBalance      = errors.New("insufficient balance for transfer")
	ErrContractAddressCollision = errors.New("contract address collision")
	ErrExecutionReverted        = errors.New("execution reverted")
	ErrMaxCodeSizeExceeded      = errors.New("max code size exceeded")
	ErrInvalidJump              = errors.New("invalid jump destination")
	ErrWriteProtection          = errors.New("write protection")
	ErrReturnDataOutOfBounds    = errors.New("return data out of bounds")
	ErrGasUintOverflow          = errors.New("gas uint64 overflow")
	ErrInvalidCode              = errors.New("invalid code: must not begin with 0xef")
)

若要完全支持evm,则在circuit设计中应支持所有可能的error类型。如使用a 10-bit lookup to check sp’s validity will be super simple,但是,这将使Prover无法为stack overflow或stack underflow等错误构建相应的proof。
这些error都将halt the call and lead to all storage updates rollback just like REVERT, 所不同的是它们将消耗所有的given gas。
因此,可将所有的错误类型做为execution result of a op来处理,让Prover来show us which result it is并 证明之。如,POP可有1种success result 和2种error result——ErrStackUnderflow和ErrOutOfGas。

Q3: How to handle dynamic gas due to access list (EIP2929)?
可在bus mapping 中额外引入一个元素 is_first_touch,用于specify which time it is being access,然后在evm circuit中,根据access time来调整gas cost。

由于 EIP2929为per EOA transaction,需在storage circuit中知悉,因此需要一个root_call_context来enable the is_first_touch flag。

5. Bus Mapping

memory circuit和stack circuit将为bus mapping lookup table 提供 valid and meaningful access record,该bus mapping lookup table将在state circuit和evm circuit中共享。

具有:

  • 唯一的gc来作为synchronizing clock
  • target 来specify the residue of the access record
  • 若有需要,有任意数量的valX

在evm circuit中,通过lookup all gc one by one and finally check the bus mapping degree is bounded to gc in the execution end,然后就可相信没有插入恶意的write。

target和相应的valX为:

  • Stack:
    – val1 - key
    – val2 - val
    – val3 - rw

  • Memory:
    – val1 - key
    – val2 - val
    – val3 - rw

  • Storage:
    – val1 - key
    – val2 - val
    – val3 - rw
    – val4 - val_prev
    – val5 - is_first_touch

对应以上solidity例子的bus mapping(按gc增序排列)为:
在这里插入图片描述
在这里插入图片描述

6. Call Context

在EOA call和internal call中,其stack和memory都是通过call_context来分隔,主要有2方面的好处:

  • caller 和 callee 不需要复制the return data or call data if not used。但使用 CALLDATACOPY 或 RETURNDATACOPY 时,仅需locate the memory by caller or callee’s call_context,and ensure they are indeed there。
  • callee可memorize caller’s call_context directly in evm circuit,然后解压回caller’s context。

具体见 appliedzkp的zkevm(4)Call context

7. Q&A

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考资料

[1] https://hackmd.io/Nwd0e5AgTVSBWRQlp-Ving
[2] 以太坊黄皮书
[3] understanding mload assembly function
[4] https://hackmd.io/kON1GVL6QOC6t5tf_OTuKA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值