|
Bytecode circuit用于约束合约的Bytecode,为其他电路提供bytecode的来源依据,其他子电路可通过Lookup约束来验证所操作bytecode码是否是合法的。
|
|
Bytecode电路用于约束智能合约的字节码,并为其他电路提供字节码的来源依据。通过Lookup约束,其他子电路可以验证操作的字节码是否合法。
|
|
|
|
|
|
Bytecode table中存放的可能并不只是一个合约的Bytecode,不同的合约的Bytecode以`address`进行标识,Bytecode与address是唯一一一对应。
|
|
在Bytecode表中存放的可能不仅仅是一个合约的字节码,不同合约的字节码以地址(address)进行标识,每个地址唯一对应一个字节码。对于一个合约的字节码来说,`pc`是opcode或者no_code(push的byte)唯一的标识。
|
|
|
|
|
|
对于一个合约的Bytecode而言,`pc`是opcode或者no_code(push的byte)唯一的标识
|
|
|
|
|
|
|
|
在对Bytecode进行处理时,将Bytecode分为两类:
|
|
在对Bytecode进行处理时,将Bytecode分为两类:
|
|
|
|
|
|
1. Opcode(非PUSH):操作指令,如ADD、SUB、CODECOPY
|
|
**Opcode(非PUSH)**:
|
|
2. Opcode(PUSH):PUSH1~PUSH32,以及PUSH的Byte
|
|
|
|
|
|
- 操作指令,如ADD、SUB、CODECOPY等。
|
|
|
|
|
|
|
|
**Opcode(PUSH)**:
|
|
|
|
|
|
|
|
- 包括PUSH1至PUSH32,以及PUSH操作码后跟随的字节。
|
|
|
|
|
|
## Witness、Column设计
|
|
## Witness、Column设计
|
|
|
|
|
|
### Witness
|
|
### Witness设计
|
|
|
|
|
|
|
|
Witness结构体`Row`包含多个字段,用于记录字节码处理过程中的相关信息:
|
|
|
|
|
|
```rust
|
|
```rust
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
|
#[derive(Clone, Debug, Default, Serialize)]
|
... | @@ -37,12 +42,17 @@ pub struct Row { |
... | @@ -37,12 +42,17 @@ pub struct Row { |
|
}
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
- cnt:如果是非PUSH的Opcode则cnt=0, 对于PUSH指令,cnt为PUSH的byte的数量,如PUSH1 --> cnt=1, PUSH2 --> cnt=2, PUSH31 --> cnt=31, PUSH32 --> cnt=32,对于no_code则cnt的值为(0~cnt-1)
|
|
- **cnt**: 如果是非 PUSH 的 Opcode 则 cnt=0,对于 PUSH 指令,cnt 为 PUSH 的 byte 的数量,如 PUSH1 --> cnt=1, PUSH2 --> cnt=2, PUSH31 --> cnt=31, PUSH32 --> cnt=32,对于 no_code 则 cnt 的值为 (0~cnt-1)。
|
|
- is_high:cnt >=16 ---> 1,cnt < 16 ---> 0,主要是用于辅助计算acc的值(规定acc的值最多为16个字节)
|
|
|
|
- acc_hi:cnt >=16的Bytecode执行此计算,`acc_hi_pre * 256 + bytecode`,即计算byte的累加值
|
|
- **is_high**: cnt >= 16 时为 1,cnt < 16 时为 0,主要用于辅助计算 acc 的值(规定 acc 的值最多为 16 个字节)。
|
|
- acc_lo:cnt < 16的Bytecode执行此计算,`acc_lo_pre * 256 + bytecode`,即计算byte的累加值
|
|
|
|
- value_hi:cnt >= 16的Bytecode的最终累加值,即最终的acc_hi
|
|
- **acc_hi**: cnt >= 16 的 Bytecode 执行此计算,`acc_hi_pre * 256 + bytecode`,即计算 byte 的累加值。
|
|
- value_lo:cnt < 15的Bytecode的最终累加值,即最终的acc_lo
|
|
|
|
|
|
- **acc_lo**: cnt < 16 的 Bytecode 执行此计算,`acc_lo_pre * 256 + bytecode`,即计算 byte 的累加值。
|
|
|
|
|
|
|
|
- **value_hi**: cnt >= 16 的 Bytecode 的最终累加值,即最终的 acc_hi。
|
|
|
|
|
|
|
|
- **value_lo**: cnt < 15 的 Bytecode 的最终累加值,即最终的 acc_lo。
|
|
|
|
|
|
注:Opcode(非PUSH)的 cnt、is_high、acc_hi、acc_lo、value_hi、value_lo值都为0
|
|
注:Opcode(非PUSH)的 cnt、is_high、acc_hi、acc_lo、value_hi、value_lo值都为0
|
|
|
|
|
... | @@ -54,7 +64,7 @@ PUSH18 0x02030405060708090a0b0c0d0e0f10111213 |
... | @@ -54,7 +64,7 @@ PUSH18 0x02030405060708090a0b0c0d0e0f10111213 |
|
ADD
|
|
ADD
|
|
```
|
|
```
|
|
|
|
|
|
假如addr为0xaa,则表格如下(这里的Opcode以字符串的形式表现比较形象):
|
|
假设 addr 为 0xaa,则表格如下:
|
|
|
|
|
|
| addr | pc | bytecode | acc_hi | acc_lo | value_hi | value_lo | cnt | is_high |
|
|
| addr | pc | bytecode | acc_hi | acc_lo | value_hi | value_lo | cnt | is_high |
|
|
| ---- | ---- | -------- | ------ | ------------------------------- | -------- | --------------------------------- | ---- | ------- |
|
|
| ---- | ---- | -------- | ------ | ------------------------------- | -------- | --------------------------------- | ---- | ------- |
|
... | @@ -81,7 +91,9 @@ ADD |
... | @@ -81,7 +91,9 @@ ADD |
|
| 0xaa | 21 | ADD | 0x0 | 0x0 | 0x0 | 0x0 | 0 | 0 |
|
|
| 0xaa | 21 | ADD | 0x0 | 0x0 | 0x0 | 0x0 | 0 | 0 |
|
|
| 0xaa | 22 | STOP | 0x0 | 0x0 | 0x0 | 0x0 | 0 | 0 |
|
|
| 0xaa | 22 | STOP | 0x0 | 0x0 | 0x0 | 0x0 | 0 | 0 |
|
|
|
|
|
|
### Circuit Column
|
|
### Column设计
|
|
|
|
|
|
|
|
Bytecode电路的配置结构体`BytecodeCircuitConfig`包含多个列和选择器,用于约束和验证字节码:
|
|
|
|
|
|
```rust
|
|
```rust
|
|
#[derive(Clone)]
|
|
#[derive(Clone)]
|
... | @@ -131,83 +143,157 @@ pub struct BytecodeCircuitConfig<F> { |
... | @@ -131,83 +143,157 @@ pub struct BytecodeCircuitConfig<F> { |
|
|
|
|
|
## 门约束
|
|
## 门约束
|
|
|
|
|
|
addr = 0 ----> Padding row(所有值都为0)
|
|
cnt=0的情况有三种:Opcode(非PUSH)、PUSH指令PUSH的最后一个byte、padding的row
|
|
|
|
|
|
cnt_prev=0 && cnt_cur !=0 ----> OPCODE(PUSH)
|
|
- **Padding行**:`addr = 0`
|
|
|
|
|
|
cnt_prev=0 && cnt_cur =0 && addr != 0 ---> OPCODE(非PUSH)
|
|
- **Opcode(PUSH)**:`cnt_prev=0 && cnt_cur !=0`
|
|
|
|
|
|
cnt_prev != 0 && cnt_cur != 0 ------> PUSH的byte(非push的最后一个字节)
|
|
- **Opcode(非PUSH)**:`cnt_prev=0 && cnt_cur =0 && addr != 0`
|
|
|
|
|
|
cnt_prev != 0 && cnt_cur == 0 -------> PUSH的最后一个字节
|
|
- **PUSH的byte**:`cnt_prev != 0 && cnt_cur != 0`
|
|
|
|
|
|
cnt=0的情况有三种:Opcode(非PUSH)、PUSH指令PUSH的最后一个byte、padding的row
|
|
- **PUSH的最后一个byte**:`cnt_prev != 0 && cnt_cur == 0`
|
|
|
|
|
|
|
|
#### **Pc 约束**
|
|
|
|
|
|
**Pc**
|
|
- 当地址发生变化时(表示新的合约开始),`pc` 应从 0 开始:
|
|
|
|
|
|
addr change ----> pc=0 (addr发生变化,说明是一个新的合约,pc应该从0开始)
|
|
```
|
|
|
|
addr change ----> pc = 0
|
|
|
|
```
|
|
|
|
|
|
addr unchange && addr != 0 ----> pc_cur - pc_prev = 1 (同一个合约中pc是累加的)
|
|
- 当地址未发生变化且地址不为 0 时(同一个合约中),`pc` 是累加的:
|
|
|
|
|
|
**Padding row**
|
|
```
|
|
|
|
addr unchange && addr != 0 ----> pc_cur - pc_prev = 1
|
|
|
|
```
|
|
|
|
|
|
cnt、addr、pc、bytecode、value_hi、value_lo、acc_hi、acc_lo、is_high, rlc_acc, hash_hi, hash_lo都为0
|
|
#### **Padding Row**
|
|
|
|
|
|
**Opcode(非PUSH)**
|
|
在填充行中,所有相关字段的值都为 0:
|
|
|
|
|
|
|
|
```
|
|
|
|
cnt、addr、pc、bytecode、value_hi、value_lo、acc_hi、acc_lo、is_high、rlc_acc、hash_hi、hash_lo 都为 0
|
|
|
|
```
|
|
|
|
|
|
cnt、value_hi、value_lo、acc_hi、acc_lo、is_high都为0
|
|
#### **Opcode(非 PUSH)**
|
|
|
|
|
|
**Opcode(PUSH)**
|
|
对于非 PUSH 的操作码,以下字段的值都为 0:
|
|
|
|
|
|
cnt !=0, acc_hi=0,acc_lo=0
|
|
```
|
|
|
|
cnt、value_hi、value_lo、acc_hi、acc_lo、is_high 都为 0
|
|
|
|
```
|
|
|
|
|
|
value_hi和value_lo由下一行进行约束,即当cnt_prev!=0时,value_hi、value_lo都和上一行相等,以此类推则PUSH指令的所有的row的value_hi和value_lo都会等于PUSH的最后一个byte的value_hi和value_lo(PUSH的最后一个byte的value_hi和value_lo是我们想要的最终的value_hi和value_lo)
|
|
#### **Opcode(PUSH)**
|
|
|
|
|
|
**cnt_prev != 0**
|
|
对于 PUSH 指令:
|
|
|
|
|
|
cnt_prev != 0说明当前行为PUSH指令PUSH的byte
|
|
- `cnt` 不为 0,`acc_hi` 和 `acc_lo` 均为 0:
|
|
|
|
|
|
cnt_prev !=0 ----> `cnt_prev - cnt_cur = 1 `(PUSH指令的cnt是递减的)
|
|
```
|
|
|
|
cnt != 0, acc_hi = 0, acc_lo = 0
|
|
|
|
```
|
|
|
|
|
|
cnt_prev !=0 -----> `is_high_prev - is_heigh_cur - cnt_is_15 = 0` (用于约束cnt=15和cat=16的分界线的cnt, 当cnt=15时, cnt_is_15=1,因为cnt>=16时is_high=1, cnt<16时is_high=0, 所以cnt_is_16_is_high - cnt_is_15_is_high =1)
|
|
- `value_hi` 和 `value_lo` 由下一行进行约束。即当 `cnt_prev != 0` 时,`value_hi` 和 `value_lo` 均与上一行相等。因此 PUSH 指令的所有行的 `value_hi` 和 `value_lo` 均等于 PUSH 的最后一个 byte 的 `value_hi` 和 `value_lo`:
|
|
|
|
|
|
cnt_prev !=0 ----> `acc_hi_prev + is_high*(acc_hi_prev*255 + bytecode) - acc_hi_cur =0`(约束acc_hi)
|
|
```
|
|
|
|
value_hi 和 value_lo 的最终值等于 PUSH 的最后一个 byte 的 value_hi 和 value_lo
|
|
|
|
```
|
|
|
|
|
|
  cnt_prev !=0 && is_high=0 ----> `acc_hi_cur-acc_hi_prev=0` (cnt < 16的行,acc_hi的值是不变的)
|
|
#### **cnt_prev != 0**
|
|
|
|
|
|
  cnt_prev !=0 && is_high=1 ----> `acc_hi_prev*256 + bytecode - acc_hi_cur =0` (即acc_hi_cur的值为`acc_pre*256+bytecoce`)
|
|
当 `cnt_prev != 0` 时,表示当前行是 PUSH 指令的字节:
|
|
|
|
|
|
cnt_prev !=0 ----> `acc_lo_cur - acc_lo_prev - (1-is_high) *(acc_lo_prev*255 + bytecode)=0` (约束acc_lo,当cnt < 16, 即high=0时,acc_lo_cur=acc_lo_prev*256 + bytecode)
|
|
- `cnt` 是递减的:
|
|
|
|
|
|
cnt_prev != 0 ----> value_hi_prev - value_hi_cur=0, value_lo_prev-value_lo_cur=0
|
|
```
|
|
|
|
cnt_prev != 0 ----> cnt_prev - cnt_cur = 1
|
|
|
|
```
|
|
|
|
|
|
**PUSH的最后一个byte:**
|
|
- 用于约束 `cnt = 15` 和 `cnt = 16` 的分界线:
|
|
|
|
|
|
cnt_prev!=0, cnt=0,value_hi=acc_hi, value_lo=acc_lo
|
|
```
|
|
|
|
cnt_prev != 0 -----> is_high_prev - is_high_cur - cnt_is_15 = 0
|
|
|
|
```
|
|
|
|
|
|
**rlc_acc**
|
|
- 约束 `acc_hi` 的值:
|
|
|
|
|
|
addr change ----> rlc_acc=bytecode (addr发生变化,说明是一个新的合约,rlc_acc开始)
|
|
```
|
|
|
|
cnt_prev != 0 ----> acc_hi_prev + is_high * (acc_hi_prev * 256 + bytecode) - acc_hi_cur = 0
|
|
|
|
```
|
|
|
|
|
|
addr unchange && addr != 0 ----> rlc_acc = rlc_acc_prev * challenge + bytecode (rlc_acc累加)
|
|
当 `is_high = 0` 时,`acc_hi` 的值不变:
|
|
|
|
|
|
**hash_hi和hash_lo**
|
|
```
|
|
|
|
cnt_prev != 0 && is_high = 0 ----> acc_hi_cur - acc_hi_prev = 0
|
|
|
|
```
|
|
|
|
|
|
addr_unchange_next != 0时,即下一行的addr change,下一行是新合约的开始,则当前行存放有合约的hash值(hash_hi和hash_lo的值不为0)
|
|
当 `is_high = 1` 时,`acc_hi_cur` 的值为 `acc_hi_prev * 256 + bytecode`:
|
|
|
|
|
|
所以只要addr_is_not_zero && addr_unchange_next != 0 则hash_hi和hash_lo都为0
|
|
```
|
|
|
|
cnt_prev != 0 && is_high = 1 ----> acc_hi_prev * 256 + bytecode - acc_hi_cur = 0
|
|
|
|
```
|
|
|
|
|
|
## Lookup约束
|
|
- 约束 `acc_lo` 的值:
|
|
|
|
|
|
每一个byte都应该在fixed电路中Lookup到,即每一个字节大小都应该在0~255范围内~~(是否真的有必要进行Lookup,因为bytecode列是从instance_bytecode来的,是公共数据)~~
|
|
```
|
|
|
|
cnt_prev != 0 ----> acc_lo_cur - acc_lo_prev - (1 - is_high) * (acc_lo_prev * 256 + bytecode) = 0
|
|
|
|
```
|
|
|
|
|
|
Lookup Fixed table, 约束Bytecode是否为正确的Opcode, <tag=Bytecode, bytecode, is_push, cnt>
|
|
当 `cnt < 16` 时,即 `is_high = 0`,`acc_lo_cur` 的值为 `acc_lo_prev * 256 + bytecode`。
|
|
|
|
|
|
Lookup keccak table,<pc+1(即length), rlc_acc, hash_hi, hash_lo> 去往Keccak table查。此lookup只在下一行的addr change==0时进行(即合约的最后一个bytecode所在行存放着合约bytecode的hash值)。
|
|
- `value_hi` 和 `value_lo` 的值保持不变:
|
|
|
|
|
|
Lookup Public table,addr和hash新增的往public table的lookup。<tag=CodeHash, addr, hash_hi, hash_lo>。此lookup只在下一行的addr change==0时进行。
|
|
```
|
|
|
|
cnt_prev != 0 ----> value_hi_prev - value_hi_cur = 0, value_lo_prev - value_lo_cur = 0
|
|
|
|
```
|
|
|
|
|
|
|
|
#### **PUSH 的最后一个 byte**
|
|
|
|
|
|
|
|
- `cnt_prev != 0` 且 `cnt = 0` 时,`value_hi` 和 `value_lo` 的值等于 `acc_hi` 和 `acc_lo`:
|
|
|
|
|
|
|
|
```
|
|
|
|
cnt_prev != 0, cnt = 0 ----> value_hi = acc_hi, value_lo = acc_lo
|
|
|
|
```
|
|
|
|
|
|
|
|
#### rlc_acc
|
|
|
|
|
|
|
|
- 当地址发生变化时,`rlc_acc` 为当前 `bytecode`:
|
|
|
|
|
|
|
|
```
|
|
|
|
addr change ----> rlc_acc = bytecode
|
|
|
|
```
|
|
|
|
|
|
|
|
- 当地址未发生变化且地址不为 0 时,`rlc_acc` 累加:
|
|
|
|
|
|
|
|
```
|
|
|
|
addr unchange && addr != 0 ----> rlc_acc = rlc_acc_prev * challenge + bytecode
|
|
|
|
```
|
|
|
|
|
|
|
|
#### **hash_hi 和 hash_lo**
|
|
|
|
|
|
|
|
- 当下一行的地址发生变化时,当前行存放有合约的哈希值:
|
|
|
|
|
|
|
|
```
|
|
|
|
addr_unchange_next != 0 ----> hash_hi 和 hash_lo 不为 0
|
|
|
|
```
|
|
|
|
|
|
|
|
- 其他情况下,`hash_hi` 和 `hash_lo` 的值均为 0:
|
|
|
|
|
|
|
|
```
|
|
|
|
addr_is_not_zero && addr_unchange_next != 0 ----> hash_hi 和 hash_lo 为 0
|
|
|
|
```
|
|
|
|
|
|
|
|
## Lookup约束
|
|
|
|
|
|
|
|
每一个byte都应该在fixed电路中Lookup到,即每一个字节大小都应该在0~255范围内。
|
|
|
|
|
|
|
|
**Lookup Fixed table**:约束Bytecode是否为正确的Opcode,格式为`<tag=Bytecode, bytecode, is_push, cnt>`。
|
|
|
|
|
|
|
|
**Lookup keccak table**:格式为`<pc+1(即length), rlc_acc, hash_hi, hash_lo>`,仅在下一行的`addr change==0`时进行。
|
|
|
|
|
|
|
|
**Lookup Public table**:格式为`<tag=CodeHash, addr, hash_hi, hash_lo>`,仅在下一行的`addr change==0`时进行。
|
|
|
|
|
|
## 2024-07-01更新
|
|
## 2024-07-01更新
|
|
|
|
|
... | @@ -242,8 +328,6 @@ bytecode可能存在一种情况,在bytecode末尾的PUSH可能存在push字 |
... | @@ -242,8 +328,6 @@ bytecode可能存在一种情况,在bytecode末尾的PUSH可能存在push字 |
|
|
|
|
|
keccak计算hash时不算这部分padding的值
|
|
keccak计算hash时不算这部分padding的值
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 新加约束
|
|
### 新加约束
|
|
|
|
|
|
length: `length_cur == length_prev`
|
|
length: `length_cur == length_prev`
|
... | | ... | |