Merge push and prover_input flags (#1417)

* Merge PUSH and PROVER_INPUT flags

* Apply comment
This commit is contained in:
Linda Guiga 2023-12-13 16:21:24 +01:00 committed by GitHub
parent 51ff8c5b51
commit bc1a3c4851
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 39 deletions

View File

@ -24,12 +24,10 @@ pub(crate) struct OpsColumnsView<T: Copy> {
pub shift: T,
/// Combines JUMPDEST and KECCAK_GENERAL flags.
pub jumpdest_keccak_general: T,
/// Flag for PROVER_INPUT.
pub prover_input: T,
/// Combines JUMP and JUMPI flags.
pub jumps: T,
/// Flag for PUSH.
pub push: T,
/// Combines PUSH and PROVER_INPUT flags.
pub push_prover_input: T,
/// Combines DUP and SWAP flags.
pub dup_swap: T,
/// Combines GET_CONTEXT and SET_CONTEXT flags.

View File

@ -24,10 +24,9 @@ const KEEPS_CONTEXT: OpsColumnsView<bool> = OpsColumnsView {
not_pop: true,
shift: true,
jumpdest_keccak_general: true,
prover_input: true,
push_prover_input: true,
jumps: true,
pc_push0: true,
push: true,
dup_swap: true,
context_op: false,
m_op_32bytes: true,

View File

@ -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; 13] = [
const NATIVE_INSTRUCTIONS: [usize; 12] = [
COL_MAP.op.binary_op,
COL_MAP.op.ternary_op,
COL_MAP.op.fp254_op,
@ -17,7 +17,7 @@ const NATIVE_INSTRUCTIONS: [usize; 13] = [
COL_MAP.op.not_pop,
COL_MAP.op.shift,
COL_MAP.op.jumpdest_keccak_general,
COL_MAP.op.prover_input,
// Not PROVER_INPUT: it is dealt with manually below.
// not JUMPS (possible need to jump)
COL_MAP.op.pc_push0,
// not PUSH (need to increment by more than 1)
@ -70,6 +70,13 @@ pub(crate) fn eval_packed_generic<P: PackedField>(
yield_constr
.constraint_transition(is_native_instruction * (lv.is_kernel_mode - nv.is_kernel_mode));
// Apply the same checks as before, for PROVER_INPUT.
let is_prover_input: P = lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES);
yield_constr.constraint_transition(
is_prover_input * (lv.program_counter - nv.program_counter + P::ONES),
);
yield_constr.constraint_transition(is_prover_input * (lv.is_kernel_mode - nv.is_kernel_mode));
// If a non-CPU cycle row is followed by a CPU cycle row, then:
// - the `program_counter` of the CPU cycle row is `main` (the entry point of our kernel),
// - execution is in kernel mode, and
@ -117,6 +124,17 @@ pub(crate) fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
let kernel_diff = builder.sub_extension(lv.is_kernel_mode, nv.is_kernel_mode);
let kernel_constr = builder.mul_extension(filter, kernel_diff);
yield_constr.constraint_transition(builder, kernel_constr);
// Same constraints as before, for PROVER_INPUT.
let is_prover_input = builder.mul_sub_extension(
lv.op.push_prover_input,
lv.opcode_bits[5],
lv.op.push_prover_input,
);
let pc_constr = builder.mul_add_extension(is_prover_input, pc_diff, is_prover_input);
yield_constr.constraint_transition(builder, pc_constr);
let kernel_constr = builder.mul_extension(is_prover_input, kernel_diff);
yield_constr.constraint_transition(builder, kernel_constr);
}
// If a non-CPU cycle row is followed by a CPU cycle row, then:

View File

@ -103,18 +103,24 @@ pub(crate) fn ctl_arithmetic_base_rows<F: Field>() -> TableWithColumns<F> {
// (also `ops` is used as the operation filter). The list of
// operations includes binary operations which will simply ignore
// the third input.
let col_bit = Column::linear_combination_with_constant(
vec![(COL_MAP.opcode_bits[5], F::NEG_ONE)],
F::ONE,
);
TableWithColumns::new(
Table::Cpu,
columns,
Some(Filter::new_simple(Column::sum([
COL_MAP.op.binary_op,
COL_MAP.op.fp254_op,
COL_MAP.op.ternary_op,
COL_MAP.op.shift,
COL_MAP.op.prover_input,
COL_MAP.op.syscall,
COL_MAP.op.exception,
]))),
Some(Filter::new(
vec![(Column::single(COL_MAP.op.push_prover_input), col_bit)],
vec![Column::sum([
COL_MAP.op.binary_op,
COL_MAP.op.fp254_op,
COL_MAP.op.ternary_op,
COL_MAP.op.shift,
COL_MAP.op.syscall,
COL_MAP.op.exception,
])],
)),
)
}
@ -201,7 +207,11 @@ pub(crate) fn ctl_data_byte_packing_push<F: Field>() -> Vec<Column<F>> {
/// CTL filter for the `PUSH` operation.
pub(crate) fn ctl_filter_byte_packing_push<F: Field>() -> Filter<F> {
Filter::new_simple(Column::single(COL_MAP.op.push))
let bit_col = Column::single(COL_MAP.opcode_bits[5]);
Filter::new(
vec![(Column::single(COL_MAP.op.push_prover_input), bit_col)],
vec![],
)
}
/// Index of the memory channel storing code.

View File

@ -3,6 +3,7 @@ use plonky2::field::packed::PackedField;
use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns::{CpuColumnsView, COL_MAP};
@ -23,7 +24,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); 7] = [
const OPCODES: [(u8, usize, bool, usize); 5] = [
// (start index of block, number of top bits to check (log2), kernel-only, flag column)
// ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly through the Arithmetic table CTL.
// ADDMOD, MULMOD and SUBMOD flags are handled partly manually here, and partly through the Arithmetic table CTL.
@ -33,9 +34,7 @@ const OPCODES: [(u8, usize, bool, usize); 7] = [
// NOT and POP are handled manually here.
// SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL.
// JUMPDEST and KECCAK_GENERAL are handled manually here.
(0x49, 0, true, COL_MAP.op.prover_input),
(0x56, 1, false, COL_MAP.op.jumps), // 0x56-0x57
(0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f
(0x80, 5, false, COL_MAP.op.dup_swap), // 0x80-0x9f
(0xf6, 1, true, COL_MAP.op.context_op), //0xf6-0xf7
(0xf9, 0, true, COL_MAP.op.exit_kernel),
@ -45,7 +44,7 @@ const OPCODES: [(u8, usize, bool, usize); 7] = [
/// List of combined opcodes requiring a special handling.
/// Each index in the list corresponds to an arbitrary combination
/// of opcodes defined in evm/src/cpu/columns/ops.rs.
const COMBINED_OPCODES: [usize; 10] = [
const COMBINED_OPCODES: [usize; 11] = [
COL_MAP.op.logic_op,
COL_MAP.op.fp254_op,
COL_MAP.op.binary_op,
@ -56,6 +55,7 @@ const COMBINED_OPCODES: [usize; 10] = [
COL_MAP.op.not_pop,
COL_MAP.op.pc_push0,
COL_MAP.op.m_op_32bytes,
COL_MAP.op.push_prover_input,
];
/// Break up an opcode (which is 8 bits long) into its eight bits.
@ -192,6 +192,16 @@ pub(crate) fn eval_packed_generic<P: PackedField>(
* (opcode - P::Scalar::from_canonical_usize(0xf8_usize))
* lv.op.m_op_32bytes;
yield_constr.constraint(op_32bytes);
// Manually check PUSH and PROVER_INPUT.
// PROVER_INPUT is a kernel-only instruction, but not PUSH.
let push_prover_input_constr = (opcode - P::Scalar::from_canonical_usize(0x49_usize))
* (opcode_high_three - P::Scalar::from_canonical_usize(0x60_usize))
* lv.op.push_prover_input;
yield_constr.constraint(push_prover_input_constr);
let prover_input_constr =
lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES) * (P::ONES - kernel_mode);
yield_constr.constraint(prover_input_constr);
}
fn opcode_high_bits_circuit<F: RichField + Extendable<D>, const D: usize>(
@ -373,4 +383,24 @@ pub(crate) fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
let constr = builder.mul_extension(mstore_32bytes_constr, mload_32bytes_constr);
let constr = builder.mul_extension(constr, lv.op.m_op_32bytes);
yield_constr.constraint(builder, constr);
// Manually check PUSH and PROVER_INPUT.
// PROVER_INPUT is a kernel-only instruction, but not PUSH.
let prover_input_opcode =
builder.constant_extension(F::Extension::from_canonical_usize(0x49usize));
let push_opcodes = builder.constant_extension(F::Extension::from_canonical_usize(0x60usize));
let push_constr = builder.sub_extension(opcode_high_three, push_opcodes);
let prover_input_constr = builder.sub_extension(opcode, prover_input_opcode);
let push_prover_input_constr =
builder.mul_many_extension([lv.op.push_prover_input, prover_input_constr, push_constr]);
yield_constr.constraint(builder, push_prover_input_constr);
let prover_input_filter = builder.mul_sub_extension(
lv.op.push_prover_input,
lv.opcode_bits[5],
lv.op.push_prover_input,
);
let constr = builder.mul_extension(prover_input_filter, is_not_kernel_mode);
yield_constr.constraint(builder, constr);
}

View File

@ -27,10 +27,9 @@ const SIMPLE_OPCODES: OpsColumnsView<Option<u32>> = OpsColumnsView {
not_pop: None, // This is handled manually below
shift: G_VERYLOW,
jumpdest_keccak_general: None, // This is handled manually below.
prover_input: KERNEL_ONLY_INSTR,
jumps: None, // Combined flag handled separately.
push_prover_input: None, // This is handled manually below.
jumps: None, // Combined flag handled separately.
pc_push0: G_BASE,
push: G_VERYLOW,
dup_swap: G_VERYLOW,
context_op: KERNEL_ONLY_INSTR,
m_op_32bytes: KERNEL_ONLY_INSTR,
@ -112,6 +111,14 @@ fn eval_packed_accumulate<P: PackedField>(
yield_constr.constraint_transition(
lv.op.jumpdest_keccak_general * (gas_diff - jumpdest_keccak_general_gas_cost),
);
// For PROVER_INPUT and PUSH operations.
// PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1.
let push_prover_input_gas_cost = lv.opcode_bits[5]
* P::Scalar::from_canonical_u32(G_VERYLOW.unwrap())
+ (P::ONES - lv.opcode_bits[5]) * P::Scalar::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap());
yield_constr
.constraint_transition(lv.op.push_prover_input * (gas_diff - push_prover_input_gas_cost));
}
fn eval_packed_init<P: PackedField>(
@ -270,6 +277,21 @@ fn eval_ext_circuit_accumulate<F: RichField + Extendable<D>, const D: usize>(
let constr = builder.mul_extension(filter, gas_diff);
yield_constr.constraint_transition(builder, constr);
// For PROVER_INPUT and PUSH operations.
// PUSH operations are differentiated from PROVER_INPUT by their 6th bit set to 1.
let push_prover_input_gas_cost = builder.arithmetic_extension(
F::from_canonical_u32(G_VERYLOW.unwrap())
- F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()),
F::from_canonical_u32(KERNEL_ONLY_INSTR.unwrap()),
lv.opcode_bits[5],
one,
one,
);
let gas_diff = builder.sub_extension(nv_lv_diff, push_prover_input_gas_cost);
let constr = builder.mul_extension(lv.op.push_prover_input, gas_diff);
yield_constr.constraint_transition(builder, constr);
}
fn eval_ext_circuit_init<F: RichField + Extendable<D>, const D: usize>(

View File

@ -28,10 +28,9 @@ pub(crate) const MIGHT_OVERFLOW: OpsColumnsView<bool> = OpsColumnsView {
not_pop: false,
shift: false,
jumpdest_keccak_general: false,
prover_input: false,
push_prover_input: true, // PROVER_INPUT doesn't require the check, but PUSH does.
jumps: false,
pc_push0: true,
push: true,
dup_swap: true,
context_op: false,
m_op_32bytes: false,
@ -119,7 +118,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = OpsCol
disable_other_channels: false,
}),
jumpdest_keccak_general: None,
prover_input: Some(StackBehavior {
push_prover_input: Some(StackBehavior {
num_pops: 0,
pushes: true,
disable_other_channels: true,
@ -130,11 +129,6 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = OpsCol
pushes: true,
disable_other_channels: true,
}),
push: Some(StackBehavior {
num_pops: 0,
pushes: true,
disable_other_channels: true,
}),
dup_swap: None,
context_op: None,
m_op_32bytes: Some(StackBehavior {

View File

@ -161,7 +161,6 @@ pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result<Operation,
fn fill_op_flag<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
let flags = &mut row.op;
*match op {
Operation::Push(1..) => &mut flags.push,
Operation::Dup(_) | Operation::Swap(_) => &mut flags.dup_swap,
Operation::Iszero | Operation::Eq => &mut flags.eq_iszero,
Operation::Not | Operation::Pop => &mut flags.not_pop,
@ -175,7 +174,7 @@ fn fill_op_flag<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
Operation::BinaryArithmetic(_) => &mut flags.binary_op,
Operation::TernaryArithmetic(_) => &mut flags.ternary_op,
Operation::KeccakGeneral | Operation::Jumpdest => &mut flags.jumpdest_keccak_general,
Operation::ProverInput => &mut flags.prover_input,
Operation::ProverInput | Operation::Push(1..) => &mut flags.push_prover_input,
Operation::Jump | Operation::Jumpi => &mut flags.jumps,
Operation::Pc | Operation::Push(0) => &mut flags.pc_push0,
Operation::GetContext | Operation::SetContext => &mut flags.context_op,
@ -189,7 +188,7 @@ fn fill_op_flag<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
const fn get_op_special_length(op: Operation) -> Option<usize> {
let behavior_opt = match op {
Operation::Push(0) | Operation::Pc => STACK_BEHAVIORS.pc_push0,
Operation::Push(1..) => STACK_BEHAVIORS.push,
Operation::Push(1..) | Operation::ProverInput => STACK_BEHAVIORS.push_prover_input,
Operation::Dup(_) | Operation::Swap(_) => STACK_BEHAVIORS.dup_swap,
Operation::Iszero => IS_ZERO_STACK_BEHAVIOR,
Operation::Not | Operation::Pop => STACK_BEHAVIORS.not_pop,
@ -206,7 +205,6 @@ const fn get_op_special_length(op: Operation) -> Option<usize> {
Operation::BinaryArithmetic(_) => STACK_BEHAVIORS.binary_op,
Operation::TernaryArithmetic(_) => STACK_BEHAVIORS.ternary_op,
Operation::KeccakGeneral | Operation::Jumpdest => STACK_BEHAVIORS.jumpdest_keccak_general,
Operation::ProverInput => STACK_BEHAVIORS.prover_input,
Operation::Jump => JUMP_OP,
Operation::Jumpi => JUMPI_OP,
Operation::GetContext | Operation::SetContext => None,
@ -229,7 +227,7 @@ const fn get_op_special_length(op: Operation) -> Option<usize> {
// Kernel-only pushing instructions aren't considered; they can't overflow.
const fn might_overflow_op(op: Operation) -> bool {
match op {
Operation::Push(1..) => MIGHT_OVERFLOW.push,
Operation::Push(1..) | Operation::ProverInput => MIGHT_OVERFLOW.push_prover_input,
Operation::Dup(_) | Operation::Swap(_) => MIGHT_OVERFLOW.dup_swap,
Operation::Iszero | Operation::Eq => MIGHT_OVERFLOW.eq_iszero,
Operation::Not | Operation::Pop => MIGHT_OVERFLOW.not_pop,
@ -245,7 +243,6 @@ const fn might_overflow_op(op: Operation) -> bool {
Operation::BinaryArithmetic(_) => MIGHT_OVERFLOW.binary_op,
Operation::TernaryArithmetic(_) => MIGHT_OVERFLOW.ternary_op,
Operation::KeccakGeneral | Operation::Jumpdest => MIGHT_OVERFLOW.jumpdest_keccak_general,
Operation::ProverInput => MIGHT_OVERFLOW.prover_input,
Operation::Jump | Operation::Jumpi => MIGHT_OVERFLOW.jumps,
Operation::Pc | Operation::Push(0) => MIGHT_OVERFLOW.pc_push0,
Operation::GetContext | Operation::SetContext => MIGHT_OVERFLOW.context_op,