diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index bab27ea8..81d8414a 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -40,9 +40,7 @@ pub struct OpsColumnsView { pub push: T, pub dup: T, pub swap: T, - // TODO: combine GET_CONTEXT and SET_CONTEXT into one flag - pub get_context: T, - pub set_context: T, + pub context_op: T, pub exit_kernel: T, // TODO: combine MLOAD_GENERAL and MSTORE_GENERAL into one flag pub mload_general: T, diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 8c5bc485..55f14820 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -15,12 +15,31 @@ fn eval_packed_get( lv: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, ) { - let filter = lv.op.get_context; + // If the opcode is GET_CONTEXT, then lv.opcode_bits[0] = 0 + let filter = lv.op.context_op * (P::ONES - lv.opcode_bits[0]); let push_channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; yield_constr.constraint(filter * (push_channel.value[0] - lv.context)); for &limb in &push_channel.value[1..] { yield_constr.constraint(filter * limb); } + + // Stack constraints + let channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; + yield_constr.constraint(filter * (channel.used - P::ONES)); + yield_constr.constraint(filter * channel.is_read); + + yield_constr.constraint(filter * (channel.addr_context - lv.context)); + yield_constr.constraint( + filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + ); + let addr_virtual = lv.stack_len; + yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); + + // Unused channels + for i in 0..NUM_GP_CHANNELS - 1 { + let channel = lv.mem_channels[i]; + yield_constr.constraint(filter * channel.used); + } } fn eval_ext_circuit_get, const D: usize>( @@ -28,7 +47,11 @@ fn eval_ext_circuit_get, const D: usize>( lv: &CpuColumnsView>, yield_constr: &mut RecursiveConstraintConsumer, ) { - let filter = lv.op.get_context; + let mut filter = lv.op.context_op; + let one = builder.one_extension(); + let minus = builder.sub_extension(one, lv.opcode_bits[0]); + filter = builder.mul_extension(filter, minus); + let push_channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; { let diff = builder.sub_extension(push_channel.value[0], lv.context); @@ -39,6 +62,45 @@ fn eval_ext_circuit_get, const D: usize>( let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } + + // Stack constraints + let channel = lv.mem_channels[NUM_GP_CHANNELS - 1]; + + { + let constr = builder.mul_sub_extension(filter, channel.used, filter); + yield_constr.constraint(builder, constr); + } + { + let constr = builder.mul_extension(filter, channel.is_read); + yield_constr.constraint(builder, constr); + } + + { + let diff = builder.sub_extension(channel.addr_context, lv.context); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + } + { + let constr = builder.arithmetic_extension( + F::ONE, + -F::from_canonical_u64(Segment::Stack as u64), + filter, + channel.addr_segment, + filter, + ); + yield_constr.constraint(builder, constr); + } + { + let diff = builder.sub_extension(channel.addr_virtual, lv.stack_len); + let constr = builder.arithmetic_extension(F::ONE, F::ZERO, filter, diff, filter); + yield_constr.constraint(builder, constr); + } + + for i in 0..NUM_GP_CHANNELS - 1 { + let channel = lv.mem_channels[i]; + let constr = builder.mul_extension(filter, channel.used); + yield_constr.constraint(builder, constr); + } } fn eval_packed_set( @@ -46,7 +108,7 @@ fn eval_packed_set( nv: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, ) { - let filter = lv.op.set_context; + let filter = lv.op.context_op * lv.opcode_bits[0]; let pop_channel = lv.mem_channels[0]; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; @@ -94,7 +156,8 @@ fn eval_ext_circuit_set, const D: usize>( nv: &CpuColumnsView>, yield_constr: &mut RecursiveConstraintConsumer, ) { - let filter = lv.op.set_context; + let mut filter = lv.op.context_op; + filter = builder.mul_extension(filter, lv.opcode_bits[0]); let pop_channel = lv.mem_channels[0]; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; diff --git a/evm/src/cpu/control_flow.rs b/evm/src/cpu/control_flow.rs index fd0688b3..0bea5c7c 100644 --- a/evm/src/cpu/control_flow.rs +++ b/evm/src/cpu/control_flow.rs @@ -8,7 +8,7 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::kernel::aggregator::KERNEL; -const NATIVE_INSTRUCTIONS: [usize; 29] = [ +const NATIVE_INSTRUCTIONS: [usize; 28] = [ COL_MAP.op.add, COL_MAP.op.mul, COL_MAP.op.sub, @@ -37,8 +37,7 @@ const NATIVE_INSTRUCTIONS: [usize; 29] = [ // not PUSH (need to increment by more than 1) COL_MAP.op.dup, COL_MAP.op.swap, - COL_MAP.op.get_context, - COL_MAP.op.set_context, + COL_MAP.op.context_op, // not EXIT_KERNEL (performs a jump) COL_MAP.op.mload_general, COL_MAP.op.mstore_general, diff --git a/evm/src/cpu/decode.rs b/evm/src/cpu/decode.rs index a2918d4c..86f2c893 100644 --- a/evm/src/cpu/decode.rs +++ b/evm/src/cpu/decode.rs @@ -22,7 +22,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP}; /// behavior. /// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to /// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid. -const OPCODES: [(u8, usize, bool, usize); 32] = [ +const OPCODES: [(u8, usize, bool, usize); 31] = [ // (start index of block, number of top bits to check (log2), kernel-only, flag column) (0x01, 0, false, COL_MAP.op.add), (0x02, 0, false, COL_MAP.op.mul), @@ -49,11 +49,10 @@ const OPCODES: [(u8, usize, bool, usize); 32] = [ (0x58, 0, false, COL_MAP.op.pc), (0x5b, 0, false, COL_MAP.op.jumpdest), (0x5f, 0, false, COL_MAP.op.push0), - (0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f - (0x80, 4, false, COL_MAP.op.dup), // 0x80-0x8f - (0x90, 4, false, COL_MAP.op.swap), // 0x90-0x9f - (0xf6, 0, true, COL_MAP.op.get_context), - (0xf7, 0, true, COL_MAP.op.set_context), + (0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f + (0x80, 4, false, COL_MAP.op.dup), // 0x80-0x8f + (0x90, 4, false, COL_MAP.op.swap), // 0x90-0x9f + (0xf6, 1, true, COL_MAP.op.context_op), // 0xf6-0xf7 (0xf9, 0, true, COL_MAP.op.exit_kernel), (0xfb, 0, true, COL_MAP.op.mload_general), (0xfc, 0, true, COL_MAP.op.mstore_general), diff --git a/evm/src/cpu/gas.rs b/evm/src/cpu/gas.rs index 75ece520..61690005 100644 --- a/evm/src/cpu/gas.rs +++ b/evm/src/cpu/gas.rs @@ -48,8 +48,7 @@ const SIMPLE_OPCODES: OpsColumnsView> = OpsColumnsView { push: G_VERYLOW, dup: G_VERYLOW, swap: G_VERYLOW, - get_context: KERNEL_ONLY_INSTR, - set_context: KERNEL_ONLY_INSTR, + context_op: KERNEL_ONLY_INSTR, exit_kernel: None, mload_general: KERNEL_ONLY_INSTR, mstore_general: KERNEL_ONLY_INSTR, diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 760f4041..8ffc152d 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -107,12 +107,7 @@ const STACK_BEHAVIORS: OpsColumnsView> = OpsColumnsView { push: None, // TODO dup: None, swap: None, - get_context: Some(StackBehavior { - num_pops: 0, - pushes: true, - disable_other_channels: true, - }), - set_context: None, // SET_CONTEXT is special since it involves the old and the new stack. + context_op: None, // SET_CONTEXT is special since it involves the old and the new stack. exit_kernel: Some(StackBehavior { num_pops: 1, pushes: false, diff --git a/evm/src/witness/gas.rs b/evm/src/witness/gas.rs index 4c7947bb..13913cce 100644 --- a/evm/src/witness/gas.rs +++ b/evm/src/witness/gas.rs @@ -42,8 +42,7 @@ pub(crate) fn gas_to_charge(op: Operation) -> u64 { Push(1..) => G_VERYLOW, Dup(_) => G_VERYLOW, Swap(_) => G_VERYLOW, - GetContext => KERNEL_ONLY_INSTR, - SetContext => KERNEL_ONLY_INSTR, + ContextOp(_) => KERNEL_ONLY_INSTR, ExitKernel => KERNEL_ONLY_INSTR, MloadGeneral => KERNEL_ONLY_INSTR, MstoreGeneral => KERNEL_ONLY_INSTR, diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 13619b96..05c22536 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -45,8 +45,7 @@ pub(crate) enum Operation { Push(u8), Dup(u8), Swap(u8), - GetContext, - SetContext, + ContextOp(bool), ExitKernel, MloadGeneral, MstoreGeneral, @@ -293,6 +292,19 @@ pub(crate) fn generate_jumpdest( Ok(()) } +pub(crate) fn generate_context_op( + is_set: bool, + state: &mut GenerationState, + row: CpuColumnsView, +) -> Result<(), ProgramError> { + // SET_CONTEXT uses mem_channels[0..=2] + if is_set { + generate_set_context(state, row) + } else { + generate_get_context(state, row) + } +} + pub(crate) fn generate_get_context( state: &mut GenerationState, mut row: CpuColumnsView, diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index a8425a66..aaf66970 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -134,8 +134,8 @@ fn decode(registers: RegistersState, opcode: u8) -> Result Ok(Operation::Syscall(opcode, 2, false)), // RETURN (0xf4, _) => Ok(Operation::Syscall(opcode, 6, false)), // DELEGATECALL (0xf5, _) => Ok(Operation::Syscall(opcode, 4, false)), // CREATE2 - (0xf6, true) => Ok(Operation::GetContext), - (0xf7, true) => Ok(Operation::SetContext), + (0xf6, true) => Ok(Operation::ContextOp(false)), // GET_CONTEXT + (0xf7, true) => Ok(Operation::ContextOp(true)), // SET_CONTEXT (0xf9, true) => Ok(Operation::ExitKernel), (0xfa, _) => Ok(Operation::Syscall(opcode, 6, false)), // STATICCALL (0xfb, true) => Ok(Operation::MloadGeneral), @@ -182,8 +182,7 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { Operation::Jump | Operation::Jumpi => &mut flags.jumps, Operation::Pc => &mut flags.pc, Operation::Jumpdest => &mut flags.jumpdest, - Operation::GetContext => &mut flags.get_context, - Operation::SetContext => &mut flags.set_context, + Operation::ContextOp(_) => &mut flags.context_op, Operation::ExitKernel => &mut flags.exit_kernel, Operation::MloadGeneral => &mut flags.mload_general, Operation::MstoreGeneral => &mut flags.mstore_general, @@ -219,8 +218,7 @@ fn perform_op( Operation::Jumpi => generate_jumpi(state, row)?, Operation::Pc => generate_pc(state, row)?, Operation::Jumpdest => generate_jumpdest(state, row)?, - Operation::GetContext => generate_get_context(state, row)?, - Operation::SetContext => generate_set_context(state, row)?, + Operation::ContextOp(is_set) => generate_context_op(is_set, state, row)?, Operation::ExitKernel => generate_exit_kernel(state, row)?, Operation::MloadGeneral => generate_mload_general(state, row)?, Operation::MstoreGeneral => generate_mstore_general(state, row)?,