A register is a location available to the zkEVM that is manipulated through the zkEVM's instructions. Registers are of different types, some of them being of generic purpose and others being specific purpose. They are also of different sizes, represented as arrays of Goldilocks prime field numbers, i.e., in the range
- Generic purpose.
- Arrays of 8 elements
[V0, V1,..., V7]
.
- Represents the State Root.
- An array of 8 elements
[V0, V1,..., V7]
.
- Represents the ConTeXt. Its main use is being able to move through the zkEVM's memory.
- Array of 1 element
[V]
.
- Represents the Stack Pointer. Its main use is being able to move through the zkEVM's memory.
- Array of 1 element
[V]
.
- Represents the Program Counter. Its main use is being able to move through the zkEVM's memory.
- Array of 1 element
[V]
.
- Represents the zk Program Counter.
- Array of 1 element
[V]
.
- Represents the Gas in a transaction.
- Array of 1 element
[V]
.
- Return Register.
- Saves the origin
zkPC
inRR
when aCALL
instruction is performed. TheRETURN
instruction loadsRR
inzkPC
. - Array of 1 element
[V]
.
- Represents the number of instructions performed within the program.
- Array of 1 element
[V]
.
- It is used to set/get bytes from Poseidon/Keccak bytes.
- Array of 1 element
[V]
.
- Used to repeat instructions.
- Array of 1 element
[V]
.
- Keeps track of the zkEVM counters:
CNT_ARITH
CNT_BINARY
CNT_KECCAK_F
CNT_SHA256_F
CNT_MEM_ALIGN
CNT_POSEIDON_G
CNT_PADDING_PG
- Arrays of 1 element
[V]
.
op = mem(addr)
addr = SP | SP++ | SP-- | SP+offset | SP-offset | SYS:E+offset | SYS:E+offset | SYS:E-offset | SYS:E | MEM:E | MEM:E+offset | MEM:E-offset | STACK:E | STACK:E+offset | STACK:E-offset | variable | variable + E | variable + E
mem(addr) = op
key0 = [C0, C1, C2, C3, C4, C5, C6, C7] key1 = [A0, A1, A2, A3, A4, A5, B0, B1] key = HP(key1, HP(key0)) op = storage.get(SR, key)
where: storage.get(root, key) -> value
key0 = [C0, C1, C2, C3, C4, C5, C6, C7] key1 = [A0, A1, A2, A3, A4, A5, B0, B1] value = [D0, D1, D2, D3, D4, D5, D6, D7] SR’ = storage.get(SR, key, value)
where: storage.set(oldRoot, key, newValue) -> newRoot
hashK[hashId][HASHPOS..HASHPOS+D-1] = op[0..D-1] HASHPOS := HASHPOS + D hashId = number | E
hashK1[hashId][HASHPOS] = op[0] HASHPOS := HASHPOS + 1
hashK[hashId].len = op
hashK[hashId].digest = op
hashS[hashId][HASHPOS..HASHPOS+D-1] = op[0..D-1] HASHPOS := HASHPOS + D hashId = number | E
hashS1[hashId][HASHPOS] = op[0] HASHPOS := HASHPOS + 1
hashS[hashId].len = op
hashS[hashId].digest = op
hashP[hashId][HASHPOS..HASHPOS+D-1] = op[0..D-1]
hashP[hashId][HASHPOS] = op[0]
hashP[hashId].len = op
hashP[hashId].digest = op
AB + C = D2**256 + op
Addition of two secp256k1 elliptic curve points (points are different) (A, B) + (C, D) = (E, op)
Addition of two secp256k1 elliptic curve points (points are equals) (A, B) + (A, B) = (E, op)
Addition of two
(A + B·u) + (C + D·u) = E + op·u
Subtraction of two
(A + B·u) - (C + D·u) = E + op·u
Multiplication of two
(A + B·u) * (C + D·u) = E + op·u
A = op
The operation is written op = A BinOp B
, where BinOp
is one of ADD,SUB,LT,SLT,EQ,AND,OR,XOR,LT4
.
Given two registers A
and B
, the instruction LT4
works by checking whether the four chunks composing A
are lower than those composing B
one-to-one.
For example, given A0,...,A7,B0,...,B7
the following check:
(A7A6 A5A4 A3A2 A1A0) LT4 (B7B6 B5B4 B3B2 B1B0)
is equivalent to:
(A7A6 < B7B6) AND (A5A4 < B5B4) AND (A3A2 < B3B2) AND (A1A0 < B1B0)
M0=A, M1=B, V=op, Offset=C
M0 = 256bit word read in position x of ZKEVM memory (32x EVM) M1 = 256bit word read in position x+1 of ZKEVM memory (32x+1 EVM) Offset = 0..31 bytes V = value of 256 bits
M0=A, M1=B, V=op Offset=C, W0=D W1=E W0 = 256bit word to write position x of ZKEVM memory (32x EVM) W1 = 256bit word to write in position x+1 of ZKEVM memory (32x+1 EVM)
M0=A, V=op, Offset=C, W0=D W0 = 256bit word to write position x of ZKEVM memory (32x EVM) V = value of 8 bits
zkPC' = jmpaddr jmpaddr = label | RR | E | reference + E | reference + RR reference = @label
JMPN: jump if op[0] was negative JMPC: jump if carry bit, only use with binary operations JMPZ: jump if op[0] was zero JMPNC: jump if no carry bit, only use with binary operations JMPNZ: jump if op[0] was different of zero
calladdr = label | reference + RR | reference + E RR' = zkPC + 1 JMP(calladdr)
JMP(RR)
Rotate the C = [C[0], C[1], ..., C[6]]
register to the left:
[op[0], op[1], ..., op[7]]= [C[7], C[0], ..., C[6]].
RCX != 0 => RCX' = RCX - 1 RCX != 0 => zkPC = zkPC REPEAT was executed at least one time
CONST, CONSTL %constname = expression
define constants const set lsr (op0) and reset the rest (op1,....,op7) constl set 8 registers (op0, op1, op2, ..,op7)