| ... | ... | @@ -287,7 +287,7 @@ JUMPDEST | 
|  |  | STOP | 
|  |  | ``` | 
|  |  |  | 
|  |  | ### Core Row | 
|  |  | ### Witness Core Row | 
|  |  |  | 
|  |  | core row中的表格设计如下: | 
|  |  |  | 
| ... | ... | @@ -304,7 +304,7 @@ cnt=1, vers[24]~vers[31]的位置用来存放去向为bytecode的LookUp, 即校 | 
|  |  | /// +---+-------+-------+-------+--------------------------+ | 
|  |  | ``` | 
|  |  |  | 
|  |  | ### 约束 | 
|  |  | ### 门约束 | 
|  |  |  | 
|  |  | 1. 当前的OPCODE=JUMP | 
|  |  | 2. Stack Value约束(tag、state_stamp、 call_id、stack_pointer、is_write) | 
| ... | ... | @@ -359,9 +359,9 @@ cnt=1, vers[24]~vers[31]的位置用来存放去向为bytecode的LookUp, 即校 | 
|  |  | } | 
|  |  | ``` | 
|  |  |  | 
|  |  | ### LookUp | 
|  |  | ### LookUp约束 | 
|  |  |  | 
|  |  | cnt=1的位置vers[24]~vers[31]的位置用来存放要lookUp的信息(next_pc的合法性,, 即校验next_pc在bytecode中是否存在) | 
|  |  | cnt=1的位置vers[24]~vers[31]的位置用来存放要lookUp的信息(next_pc的合法性,即校验next_pc在bytecode中是否存在) | 
|  |  |  | 
|  |  | 来源:core | 
|  |  |  | 
| ... | ... | @@ -466,6 +466,344 @@ fn gen_witness(&self, trace: &GethExecStep, current_state: &mut WitnessExecHelpe | 
|  |  | } | 
|  |  | ``` | 
|  |  |  | 
|  |  | ## CODECOPY/EXTCODECOPY | 
|  |  |  | 
|  |  | ### 概述 | 
|  |  |  | 
|  |  | #### CODECOPY | 
|  |  |  | 
|  |  | 概述:该操作通常用于在智能合约中实现一些动态代码加载或代码复制的逻辑,将合约中字节码复制到Memory中,即将合约代码复制到内存的操作。 | 
|  |  |  | 
|  |  | 具体操作:从栈顶弹出三个值,分别为:destOffset、offset、length,根据当前三个值进行操作:`memory[destOffset:destOffset+length] =    address(this).code[offset:offset+length]`,即从code中以offset的位置开始,复制长度为length的字节码到Memory以destOffset为起始的位置(都是以字节为单位进行操作) | 
|  |  |  | 
|  |  | trace示例:CODECOPY指令执行时,会从栈顶弹出三个值:destOffset、offset、length,然后根据这三个值进行合约代码的复制 | 
|  |  |  | 
|  |  | ```shell | 
|  |  | PUSH1 0xa | 
|  |  | PUSH30 0x02030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f | 
|  |  | ADD | 
|  |  | PUSH1 0x1E | 
|  |  | PUSH1 0x03 | 
|  |  | PUSH1 0x00 | 
|  |  | CODECOPY | 
|  |  | STOP | 
|  |  | ``` | 
|  |  |  | 
|  |  | 此操作会从合约代码第3个字节开始复制,长度为30,复制到Memory以00起始的位置,即将`02030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f` 复制到Memory中 | 
|  |  |  | 
|  |  | #### EXTCODECOPY | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  | ### Witness Code Row | 
|  |  |  | 
|  |  | core row的表格设计如下: | 
|  |  |  | 
|  |  | cnt=2, vers[0]~vers[8] 为正常copy row进行LookUp的值 | 
|  |  |  | 
|  |  | cnt=2, vers[8]~vers[17] 为padding copy row进行LookUp的值 | 
|  |  |  | 
|  |  | ```shell | 
|  |  | /// +---+-------+-------+-------+----------+ | 
|  |  | /// |cnt| 8 col | 8 col | 8 col | 8 col    | | 
|  |  | /// +---+-------+-------+-------+----------+ | 
|  |  | /// | 2 | COPY(9) | ZEROCOPY(9)| | 
|  |  | /// | 1 | STATE | STATE | STATE |  STATE   | | 
|  |  | /// | 0 | DYNA_SELECTOR   | AUX            | | 
|  |  | /// +---+-------+-------+-------+----------+ | 
|  |  | ``` | 
|  |  |  | 
|  |  | #### 解释 | 
|  |  |  | 
|  |  | 在进行copy时,offset即是要复制的合约的代码的起始位置start,复制长度为length,复制的结束位置end: start+length,但是在进行copy的时候可能存在边界问题,如下: | 
|  |  |  | 
|  |  | 1)offset<=len(code), copy_len > 0, offset+copy_len <= len(code), 无需进行0填充 | 
|  |  |  | 
|  |  | 2)offset<=len(code), copy_len == 0, offset+copy_len <= len(code), 无需0填充, 也无需拷贝,即code[start:start],为空 | 
|  |  |  | 
|  |  | 3)offset<=len(code), copy_len>0, offset_copy_len > len(code),需要进行0填充,填充长度为 copy_len - (len(code)-offset) --> offset+copy_len - len(code) | 
|  |  |  | 
|  |  | 4)offset>len(code), copy_len>=0, offset+copy_len > len(code), 需要进行0填充,填充长度为offset+copy_len-len(code) | 
|  |  |  | 
|  |  | 此外当 offset > u64时,会对offset进行特殊处理为u64的最大值 | 
|  |  |  | 
|  |  | 对于Padding的值也会被放到Memory中,Padding的值,Src部分都是默认值,dst部分正常填充(对Copy子电路来说,Padding部分,因为ByteCode中并不存在Padding的值,所以不存在来源,但是Padding的值也会被放入到Memory中,所以存在去向,所以Padding部分的LookUp无来源,但是有去向为State)。 | 
|  |  |  | 
|  |  | 不过padding中的dst部分在进行赋值时需要注意:dst_pointer和dst_stamp,因为在Copy子电路中,memory_addr=dst_pointer+cnt,memory_stamp=dst_stamp+cnt,而Padding和正常的Copy是分成两部分的(Padding在Copy子电路中的Type为Zero),即Padding在Copy子电路中的cnt也是从0开始计数的。 | 
|  |  |  | 
|  |  | 举例如下: | 
|  |  |  | 
|  |  | > 合约代码长度:len(code)=5 | 
|  |  | > 进行CodeCopy操作: | 
|  |  | > offset: 0 | 
|  |  | > copy_len: 8 | 
|  |  | > destOffset: 0 | 
|  |  | > 进行CodeCopy, 因为offset+copy_len > len(code) 所以需要填充0,填充长度为offset+copy_len - len(code) = 3, 所以整体CopyRow的生成就分成了两部分:正常的CopyRow5行,PaddingCopyRow3行。 | 
|  |  | > 加入在生成Copy时的stamp为1记为OriginalStamp | 
|  |  | > 则NoPaddingCopyRow的Stamp都为1(OriginalStamp),dest_pointer为destOffset+{0..4}, cnt为0~4 | 
|  |  | > 则PaddingCopyRow的Stamp为6(OriginalStamp+len(code)), dest_pointer为destOffset+len(code)+{0..2}, cnt为0~2 | 
|  |  | > 因为是分为两部分的,所以stamp、dest_pointer不是相同的 | 
|  |  |  | 
|  |  | 代码如下: | 
|  |  |  | 
|  |  | ```rust | 
|  |  | pub fn get_code_copy_rows_new( | 
|  |  | &mut self, | 
|  |  | address: U256, | 
|  |  | dst: U256, | 
|  |  | src: U256, | 
|  |  | len: U256, | 
|  |  | ) -> (Vec<copy::Row>, Vec<state::Row>, u64, u64, u64) { | 
|  |  | //... | 
|  |  |  | 
|  |  | let codecopy_stamp = self.state_stamp; | 
|  |  | if code_copy_length > 0 { | 
|  |  | for i in 0..code_copy_length { | 
|  |  | let code = self.bytecode.get(&address).unwrap(); | 
|  |  | let byte = code.get((src_offset + i) as usize).unwrap().value; | 
|  |  | copy_rows.push(copy::Row { | 
|  |  | byte: byte.into(), | 
|  |  | src_type: copy::Type::Bytecode, | 
|  |  | src_id: address, | 
|  |  | src_pointer: src_offset.into(), | 
|  |  | src_stamp: None, | 
|  |  | dst_type: copy::Type::Memory, | 
|  |  | dst_id: self.call_id.into(), | 
|  |  | dst_pointer: dst_offset.into(), | 
|  |  | dst_stamp: codecopy_stamp.into(), | 
|  |  | cnt: i.into(), | 
|  |  | len: code_copy_length.into(), | 
|  |  | }); | 
|  |  | state_rows.push(self.get_memory_write_row((dst_offset + i) as usize, byte)); | 
|  |  | } | 
|  |  | } | 
|  |  |  | 
|  |  | let codecopy_padding_stamp = self.state_stamp; | 
|  |  | if padding_length > 0 { | 
|  |  | for i in 0..padding_length { | 
|  |  | state_rows.push( | 
|  |  | self.get_memory_write_row( | 
|  |  | (dst_offset + code_copy_length + i) as usize, | 
|  |  | 0 as u8, | 
|  |  | ), | 
|  |  | ); | 
|  |  | copy_rows.push(copy::Row { | 
|  |  | byte: 0.into(), | 
|  |  | src_type: copy::Type::default(), | 
|  |  | src_id: 0.into(), | 
|  |  | src_pointer: 0.into(), | 
|  |  | src_stamp: None, | 
|  |  | dst_type: copy::Type::Memory, | 
|  |  | dst_id: self.call_id.into(), | 
|  |  | dst_pointer: (dst_offset + code_copy_length).into(), | 
|  |  | dst_stamp: codecopy_padding_stamp.into(), | 
|  |  | cnt: i.into(), | 
|  |  | len: U256::from(padding_length), | 
|  |  | }) | 
|  |  | } | 
|  |  | } | 
|  |  | // ... | 
|  |  | } | 
|  |  | ``` | 
|  |  |  | 
|  |  | 此外还需要注意**copy_len为0**的特殊值,当copy_len为0时,NoPaddingCopyRow行数等于0, PaddingCopyRow行数等于0,则CoreRow cnt=2的值会存放两个全是默认值的CopyRow(即全为0的值)放在vers[0]~vers[17],当NoPaddingCopyRow行数大于0,PaddingCopyRow行数等于0时,vers[8]~vers[17]会存放全是默认值当CopyRow(即全为0的值) | 
|  |  |  | 
|  |  | 并且行数为0的情况并不会放到State和Copy子电路的表中,因为copy_len为0,理应该不做任何操作,代码示例如下: | 
|  |  |  | 
|  |  | ```rust | 
|  |  | fn gen_witness(&self, trace: &GethExecStep, current_state: &mut WitnessExecHelper) -> Witness { | 
|  |  | let (stack_pop_0, address) = current_state.get_pop_stack_row_value(&trace); | 
|  |  | assert!(address.leading_zeros() >= ADDRESS_ZERO_COUNT); | 
|  |  | //let address_code = current_state.bytecode.get(&address).unwrap(); | 
|  |  | let (stack_pop_1, mem_offset) = current_state.get_pop_stack_row_value(&trace); | 
|  |  |  | 
|  |  | let (stack_pop_2, code_offset) = current_state.get_pop_stack_row_value(&trace); | 
|  |  |  | 
|  |  | let (stack_pop_3, size) = current_state.get_pop_stack_row_value(&trace); | 
|  |  |  | 
|  |  | let mut core_row_2 = current_state.get_core_row_without_versatile(&trace, 2); | 
|  |  |  | 
|  |  | let (copy_rows, mem_rows, input_length, padding_length, code_copy_length) = | 
|  |  | current_state.get_code_copy_rows_new(address, mem_offset, code_offset, size); | 
|  |  |  | 
|  |  | let mut default_copy_row = ¤t_state.get_none_copy_row(); | 
|  |  | if input_length > 0 && code_copy_length > 0 { | 
|  |  | default_copy_row = ©_rows[0]; | 
|  |  | } | 
|  |  | let mut default_padding_row = ¤t_state.get_none_padding_copy_row(); | 
|  |  | if input_length > 0 && padding_length > 0 { | 
|  |  | default_padding_row = ©_rows[code_copy_length as usize] | 
|  |  | } | 
|  |  |  | 
|  |  | core_row_2.insert_copy_lookup_new( | 
|  |  | default_copy_row, | 
|  |  | default_padding_row, | 
|  |  | code_copy_length, | 
|  |  | padding_length, | 
|  |  | ); | 
|  |  |  | 
|  |  | let mut core_row_1 = current_state.get_core_row_without_versatile(&trace, 1); | 
|  |  |  | 
|  |  | core_row_1.insert_state_lookups([&stack_pop_0, &stack_pop_1, &stack_pop_2, &stack_pop_3]); | 
|  |  | let core_row_0 = ExecutionState::EXTCODECOPY.into_exec_state_core_row( | 
|  |  | trace, | 
|  |  | current_state, | 
|  |  | NUM_STATE_HI_COL, | 
|  |  | NUM_STATE_LO_COL, | 
|  |  | ); | 
|  |  |  | 
|  |  | let mut state_vec = vec![stack_pop_0, stack_pop_1, stack_pop_2, stack_pop_3]; | 
|  |  | if mem_rows.len() > 0 { | 
|  |  | state_vec.extend(mem_rows); | 
|  |  | } | 
|  |  | if input_length > 0 { | 
|  |  | Witness { | 
|  |  | copy: copy_rows, | 
|  |  | core: vec![core_row_2, core_row_1, core_row_0], | 
|  |  | state: state_vec, | 
|  |  | ..Default::default() | 
|  |  | } | 
|  |  | } else { | 
|  |  | Witness { | 
|  |  | core: vec![core_row_2, core_row_1, core_row_0], | 
|  |  | state: state_vec, | 
|  |  | ..Default::default() | 
|  |  | } | 
|  |  | } | 
|  |  | } | 
|  |  | ``` | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  | ### 门约束 | 
|  |  |  | 
|  |  | ### LookUp约束 | 
|  |  |  | 
|  |  | LookUp约束被放在cnt=2的CopyRow,被分为两个种约束:NoPaddingCopy的约束和PaddingCopy约束,各占9列: vers[0]~vers[8], vers[9]~vers[17] | 
|  |  |  | 
|  |  | 参考Witness Code Row中的解释部分,当offset+copy_len > len(code)时,需要填充0,填充长度为offset+copy_len-len(code)。 | 
|  |  |  | 
|  |  | 来源:Core | 
|  |  |  | 
|  |  | 去向:Copy | 
|  |  |  | 
|  |  | 特别注意:当copy_len为0或者padding_len为0时,为0的CopyRow(都是默认值,全为零值)也是需要进行LookUp的(LookUp Copy中全为默认值行) | 
|  |  |  | 
|  |  | 可参考代码如下: | 
|  |  |  | 
|  |  | #### insert_copy_lookup | 
|  |  |  | 
|  |  | 在copy row cnt=2的位置分别NoPaddingCopyRow(vers[0]~vers[8])和PaddingCopyRow(vers[9]~vers[17])要lookUp的值 | 
|  |  |  | 
|  |  | ```rust | 
|  |  | fn gen_witness(&self, trace: &GethExecStep, current_state: &mut WitnessExecHelper) -> Witness { | 
|  |  | // ... | 
|  |  |  | 
|  |  | // 生成copy_rows | 
|  |  | let (copy_rows, mem_rows, input_length, padding_length, code_copy_length) = | 
|  |  | current_state.get_code_copy_rows_new(address, mem_offset, code_offset, size); | 
|  |  |  | 
|  |  | // default_copy_row:全为默认值的CopyRow | 
|  |  | let mut default_copy_row = ¤t_state.get_none_copy_row(); | 
|  |  | if input_length > 0 && code_copy_length > 0 { | 
|  |  | default_copy_row = ©_rows[0]; | 
|  |  | } | 
|  |  | // default_padding_row:全为默认值的CopyRow | 
|  |  | let mut default_padding_row = ¤t_state.get_none_copy_row(); | 
|  |  | if input_length > 0 && padding_length > 0 { | 
|  |  | default_padding_row = ©_rows[code_copy_length as usize] | 
|  |  | } | 
|  |  |  | 
|  |  | core_row_2.insert_copy_lookup_new( | 
|  |  | default_copy_row, | 
|  |  | default_padding_row, | 
|  |  | code_copy_length, | 
|  |  | padding_length, | 
|  |  | ); | 
|  |  | // ... | 
|  |  | } | 
|  |  |  | 
|  |  | pub fn insert_copy_lookup_new( | 
|  |  | &mut self, | 
|  |  | copy: ©::Row, | 
|  |  | padding_copy: ©::Row, | 
|  |  | copy_length: u64, | 
|  |  | padding_length: u64, | 
|  |  | ) { | 
|  |  | // | 
|  |  | assert_eq!(self.cnt, 2.into()); | 
|  |  | for (cell, value) in [ | 
|  |  | // code copy | 
|  |  | (&mut self.vers_0, Some((copy.src_type as u8).into())), | 
|  |  | (&mut self.vers_1, Some(copy.src_id)), | 
|  |  | (&mut self.vers_2, Some(copy.src_pointer)), | 
|  |  | (&mut self.vers_3, copy.src_stamp), | 
|  |  | (&mut self.vers_4, Some((copy.dst_type as u8).into())), | 
|  |  | (&mut self.vers_5, Some(copy.dst_id)), | 
|  |  | (&mut self.vers_6, Some(copy.dst_pointer)), | 
|  |  | (&mut self.vers_7, Some(copy.dst_stamp)), | 
|  |  | (&mut self.vers_8, Some(copy.len)), | 
|  |  | // padding copy | 
|  |  | (&mut self.vers_9, Some((padding_copy.src_type as u8).into())), | 
|  |  | (&mut self.vers_10, Some(padding_copy.src_id)), | 
|  |  | (&mut self.vers_11, Some(padding_copy.src_pointer)), | 
|  |  | (&mut self.vers_12, padding_copy.src_stamp), | 
|  |  | ( | 
|  |  | &mut self.vers_13, | 
|  |  | Some((padding_copy.dst_type as u8).into()), | 
|  |  | ), | 
|  |  | (&mut self.vers_14, Some(padding_copy.dst_id)), | 
|  |  | (&mut self.vers_15, Some(padding_copy.dst_pointer)), | 
|  |  | (&mut self.vers_16, Some(padding_copy.dst_stamp)), | 
|  |  | (&mut self.vers_17, Some(padding_copy.len)), | 
|  |  | // (&mut self.vers_18, Some(U256::from(copy_length))), | 
|  |  | // (&mut self.vers_19, Some(U256::from(padding_length))), | 
|  |  | // | 
|  |  | ] { | 
|  |  | // before inserting, these columns must be none | 
|  |  | assert!(cell.is_none()); | 
|  |  | *cell = value; | 
|  |  | } | 
|  |  | // .... | 
|  |  | } | 
|  |  |  | 
|  |  | ``` | 
|  |  |  | 
|  |  | #### get_lookups | 
|  |  |  | 
|  |  | 从copy row cnt=2的位置分别去NoPaddingCopyRow: vers[0]~vers[8],PaddingCopyRow: vers[9]~vers[17] | 
|  |  |  | 
|  |  | ```rust | 
|  |  | fn get_lookups( | 
|  |  | &self, | 
|  |  | config: &ExecutionConfig<F, NUM_STATE_HI_COL, NUM_STATE_LO_COL>, | 
|  |  | meta: &mut ConstraintSystem<F>, | 
|  |  | ) -> Vec<(String, LookupEntry<F>)> { | 
|  |  | let stack_lookup_0 = query_expression(meta, |meta| config.get_state_lookup(meta, 0)); | 
|  |  | let stack_lookup_1 = query_expression(meta, |meta| config.get_state_lookup(meta, 1)); | 
|  |  | let stack_lookup_2 = query_expression(meta, |meta| config.get_state_lookup(meta, 2)); | 
|  |  | let stack_lookup_3 = query_expression(meta, |meta| config.get_state_lookup(meta, 3)); | 
|  |  | let copy_lookup = query_expression(meta, |meta| config.get_copy_lookup(meta)); | 
|  |  | let padding_copy_lookup = | 
|  |  | query_expression(meta, |meta| config.get_copy_padding_lookup(meta)); | 
|  |  |  | 
|  |  | vec![ | 
|  |  | ("stack pop account address".into(), stack_lookup_0), | 
|  |  | ("stack pop mem offset".into(), stack_lookup_1), | 
|  |  | ("stack pop code offset".into(), stack_lookup_2), | 
|  |  | ("stack pop length".into(), stack_lookup_3), | 
|  |  | ("copy look up".into(), copy_lookup), | 
|  |  | ("padding look up".into(), padding_copy_lookup), | 
|  |  | ] | 
|  |  | } | 
|  |  | ``` | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
| ... | ... |  |