mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-05 07:13:08 +00:00
Memory channel for program counter (#717)
This commit is contained in:
parent
c27e40e7bb
commit
084700a7f4
@ -1,3 +1,5 @@
|
||||
use std::iter;
|
||||
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::field::types::Field;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
@ -5,6 +7,7 @@ use plonky2::hash::hash_types::RichField;
|
||||
use crate::config::StarkConfig;
|
||||
use crate::cpu::cpu_stark;
|
||||
use crate::cpu::cpu_stark::CpuStark;
|
||||
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||
use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns};
|
||||
use crate::keccak::keccak_stark;
|
||||
use crate::keccak::keccak_stark::KeccakStark;
|
||||
@ -13,8 +16,8 @@ use crate::keccak_memory::keccak_memory_stark;
|
||||
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
|
||||
use crate::logic;
|
||||
use crate::logic::LogicStark;
|
||||
use crate::memory::memory_stark;
|
||||
use crate::memory::memory_stark::MemoryStark;
|
||||
use crate::memory::{memory_stark, NUM_CHANNELS};
|
||||
use crate::stark::Stark;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -129,11 +132,16 @@ fn ctl_logic<F: Field>() -> CrossTableLookup<F> {
|
||||
}
|
||||
|
||||
fn ctl_memory<F: Field>() -> CrossTableLookup<F> {
|
||||
let cpu_memory_ops = (0..NUM_CHANNELS).map(|channel| {
|
||||
let cpu_memory_code_read = TableWithColumns::new(
|
||||
Table::Cpu,
|
||||
cpu_stark::ctl_data_code_memory(),
|
||||
Some(cpu_stark::ctl_filter_code_memory()),
|
||||
);
|
||||
let cpu_memory_gp_ops = (0..NUM_GP_CHANNELS).map(|channel| {
|
||||
TableWithColumns::new(
|
||||
Table::Cpu,
|
||||
cpu_stark::ctl_data_memory(channel),
|
||||
Some(cpu_stark::ctl_filter_memory(channel)),
|
||||
cpu_stark::ctl_data_gp_memory(channel),
|
||||
Some(cpu_stark::ctl_filter_gp_memory(channel)),
|
||||
)
|
||||
});
|
||||
let keccak_memory_reads = (0..KECCAK_WIDTH_BYTES).map(|i| {
|
||||
@ -150,7 +158,8 @@ fn ctl_memory<F: Field>() -> CrossTableLookup<F> {
|
||||
Some(keccak_memory_stark::ctl_filter()),
|
||||
)
|
||||
});
|
||||
let all_lookers = cpu_memory_ops
|
||||
let all_lookers = iter::once(cpu_memory_code_read)
|
||||
.chain(cpu_memory_gp_ops)
|
||||
.chain(keccak_memory_reads)
|
||||
.chain(keccak_memory_writes)
|
||||
.collect();
|
||||
@ -725,6 +734,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Ignoring but not deleting so the test can serve as an API usage example
|
||||
fn test_all_stark() -> Result<()> {
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
let (all_stark, proof) = get_proof(&config)?;
|
||||
@ -732,6 +742,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Ignoring but not deleting so the test can serve as an API usage example
|
||||
fn test_all_stark_recursive_verifier() -> Result<()> {
|
||||
init_logger();
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ use std::mem::{size_of, transmute};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::cpu::columns::general::CpuGeneralColumnsView;
|
||||
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||
use crate::memory;
|
||||
use crate::util::{indices_arr, transmute_no_compile_time_size_checks};
|
||||
|
||||
@ -35,6 +36,13 @@ pub struct CpuColumnsView<T: Copy> {
|
||||
/// Lets us re-use columns in non-cycle rows.
|
||||
pub is_cpu_cycle: T,
|
||||
|
||||
/// If CPU cycle: Current context.
|
||||
// TODO: this is currently unconstrained
|
||||
pub context: T,
|
||||
|
||||
/// If CPU cycle: Context for code memory channel.
|
||||
pub code_context: T,
|
||||
|
||||
/// If CPU cycle: The program counter for the current instruction.
|
||||
pub program_counter: T,
|
||||
|
||||
@ -159,7 +167,7 @@ pub struct CpuColumnsView<T: Copy> {
|
||||
pub(crate) general: CpuGeneralColumnsView<T>,
|
||||
|
||||
pub(crate) clock: T,
|
||||
pub mem_channels: [MemoryChannelView<T>; memory::NUM_CHANNELS],
|
||||
pub mem_channels: [MemoryChannelView<T>; NUM_GP_CHANNELS],
|
||||
}
|
||||
|
||||
// `u8` is guaranteed to have a `size_of` of 1.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::iter::repeat;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use itertools::Itertools;
|
||||
@ -10,10 +11,11 @@ use plonky2::hash::hash_types::RichField;
|
||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||
use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS};
|
||||
use crate::cpu::{
|
||||
bootstrap_kernel, control_flow, decode, jumps, simple_logic, stack_bounds, syscalls,
|
||||
bootstrap_kernel, control_flow, decode, jumps, membus, simple_logic, stack_bounds, syscalls,
|
||||
};
|
||||
use crate::cross_table_lookup::Column;
|
||||
use crate::memory::NUM_CHANNELS;
|
||||
use crate::memory::segments::Segment;
|
||||
use crate::memory::{NUM_CHANNELS, VALUE_LIMBS};
|
||||
use crate::stark::Stark;
|
||||
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
|
||||
|
||||
@ -25,14 +27,13 @@ pub fn ctl_data_keccak<F: Field>() -> Vec<Column<F>> {
|
||||
}
|
||||
|
||||
pub fn ctl_data_keccak_memory<F: Field>() -> Vec<Column<F>> {
|
||||
// When executing KECCAK_GENERAL, the memory channels are used as follows:
|
||||
// channel 0: instruction
|
||||
// channel 1: stack[-1] = context
|
||||
// channel 2: stack[-2] = segment
|
||||
// channel 3: stack[-3] = virtual
|
||||
let context = Column::single(COL_MAP.mem_channels[1].value[0]);
|
||||
let segment = Column::single(COL_MAP.mem_channels[2].value[0]);
|
||||
let virt = Column::single(COL_MAP.mem_channels[3].value[0]);
|
||||
// When executing KECCAK_GENERAL, the GP memory channels are used as follows:
|
||||
// GP channel 0: stack[-1] = context
|
||||
// GP channel 1: stack[-2] = segment
|
||||
// GP channel 2: stack[-3] = virtual
|
||||
let context = Column::single(COL_MAP.mem_channels[0].value[0]);
|
||||
let segment = Column::single(COL_MAP.mem_channels[1].value[0]);
|
||||
let virt = Column::single(COL_MAP.mem_channels[2].value[0]);
|
||||
|
||||
let num_channels = F::from_canonical_usize(NUM_CHANNELS);
|
||||
let clock = Column::linear_combination([(COL_MAP.clock, num_channels)]);
|
||||
@ -60,29 +61,57 @@ pub fn ctl_filter_logic<F: Field>() -> Column<F> {
|
||||
Column::sum([COL_MAP.is_and, COL_MAP.is_or, COL_MAP.is_xor])
|
||||
}
|
||||
|
||||
pub fn ctl_data_memory<F: Field>(channel: usize) -> Vec<Column<F>> {
|
||||
debug_assert!(channel < NUM_CHANNELS);
|
||||
pub const MEM_CODE_CHANNEL_IDX: usize = 0;
|
||||
pub const MEM_GP_CHANNELS_IDX_START: usize = MEM_CODE_CHANNEL_IDX + 1;
|
||||
|
||||
/// Make the time/channel column for memory lookups.
|
||||
fn mem_time_and_channel<F: Field>(channel: usize) -> Column<F> {
|
||||
let scalar = F::from_canonical_usize(NUM_CHANNELS);
|
||||
let addend = F::from_canonical_usize(channel);
|
||||
Column::linear_combination_with_constant([(COL_MAP.clock, scalar)], addend)
|
||||
}
|
||||
|
||||
pub fn ctl_data_code_memory<F: Field>() -> Vec<Column<F>> {
|
||||
let mut cols = vec![
|
||||
Column::constant(F::ONE), // is_read
|
||||
Column::single(COL_MAP.code_context), // addr_context
|
||||
Column::constant(F::from_canonical_u64(Segment::Code as u64)), // addr_segment
|
||||
Column::single(COL_MAP.program_counter), // addr_virtual
|
||||
];
|
||||
|
||||
// Low limb of the value matches the opcode bits
|
||||
cols.push(Column::le_bits(COL_MAP.opcode_bits));
|
||||
|
||||
// High limbs of the value are all zero.
|
||||
cols.extend(repeat(Column::constant(F::ZERO)).take(VALUE_LIMBS - 1));
|
||||
|
||||
cols.push(mem_time_and_channel(MEM_CODE_CHANNEL_IDX));
|
||||
|
||||
cols
|
||||
}
|
||||
|
||||
pub fn ctl_data_gp_memory<F: Field>(channel: usize) -> Vec<Column<F>> {
|
||||
let channel_map = COL_MAP.mem_channels[channel];
|
||||
let mut cols: Vec<Column<F>> = Column::singles([
|
||||
let mut cols: Vec<_> = Column::singles([
|
||||
channel_map.is_read,
|
||||
channel_map.addr_context,
|
||||
channel_map.addr_segment,
|
||||
channel_map.addr_virtual,
|
||||
])
|
||||
.collect_vec();
|
||||
.collect();
|
||||
|
||||
cols.extend(Column::singles(channel_map.value));
|
||||
|
||||
let scalar = F::from_canonical_usize(NUM_CHANNELS);
|
||||
let addend = F::from_canonical_usize(channel);
|
||||
cols.push(Column::linear_combination_with_constant(
|
||||
[(COL_MAP.clock, scalar)],
|
||||
addend,
|
||||
));
|
||||
cols.push(mem_time_and_channel(MEM_GP_CHANNELS_IDX_START + channel));
|
||||
|
||||
cols
|
||||
}
|
||||
|
||||
pub fn ctl_filter_memory<F: Field>(channel: usize) -> Column<F> {
|
||||
pub fn ctl_filter_code_memory<F: Field>() -> Column<F> {
|
||||
Column::single(COL_MAP.is_cpu_cycle)
|
||||
}
|
||||
|
||||
pub fn ctl_filter_gp_memory<F: Field>(channel: usize) -> Column<F> {
|
||||
Column::single(COL_MAP.mem_channels[channel].used)
|
||||
}
|
||||
|
||||
@ -95,6 +124,7 @@ impl<F: RichField, const D: usize> CpuStark<F, D> {
|
||||
pub fn generate(&self, local_values: &mut [F; NUM_CPU_COLUMNS]) {
|
||||
let local_values: &mut CpuColumnsView<_> = local_values.borrow_mut();
|
||||
decode::generate(local_values);
|
||||
membus::generate(local_values);
|
||||
simple_logic::generate(local_values);
|
||||
stack_bounds::generate(local_values); // Must come after `decode`.
|
||||
}
|
||||
@ -117,6 +147,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
control_flow::eval_packed_generic(local_values, next_values, yield_constr);
|
||||
decode::eval_packed_generic(local_values, yield_constr);
|
||||
jumps::eval_packed(local_values, next_values, yield_constr);
|
||||
membus::eval_packed(local_values, yield_constr);
|
||||
simple_logic::eval_packed(local_values, yield_constr);
|
||||
stack_bounds::eval_packed(local_values, yield_constr);
|
||||
syscalls::eval_packed(local_values, next_values, yield_constr);
|
||||
@ -134,6 +165,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
decode::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
jumps::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
membus::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
simple_logic::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
stack_bounds::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
|
||||
70
evm/src/cpu/membus.rs
Normal file
70
evm/src/cpu/membus.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::field::packed::PackedField;
|
||||
use plonky2::field::types::PrimeField64;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::iop::ext_target::ExtensionTarget;
|
||||
|
||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||
use crate::cpu::columns::CpuColumnsView;
|
||||
|
||||
/// General-purpose memory channels; they can read and write to all contexts/segments/addresses.
|
||||
pub const NUM_GP_CHANNELS: usize = 4;
|
||||
|
||||
pub mod channel_indices {
|
||||
use std::ops::Range;
|
||||
|
||||
pub const CODE: usize = 0;
|
||||
pub const GP: Range<usize> = CODE + 1..(CODE + 1) + super::NUM_GP_CHANNELS;
|
||||
}
|
||||
|
||||
/// Total memory channels used by the CPU table. This includes all the `GP_MEM_CHANNELS` as well as
|
||||
/// all special-purpose memory channels.
|
||||
///
|
||||
/// Currently, there is one special-purpose memory channel, which reads the opcode from memory. Its
|
||||
/// limitations are:
|
||||
/// - it is enabled by `is_cpu_cycle`,
|
||||
/// - it always reads and cannot write,
|
||||
/// - the context is derived from the current context and the `is_kernel_mode` flag,
|
||||
/// - the segment is hard-wired to the code segment,
|
||||
/// - the address is `program_counter`,
|
||||
/// - the value must fit in one byte (in the least-significant position) and its eight bits are
|
||||
/// found in `opcode_bits`.
|
||||
/// These limitations save us numerous columns in the CPU table.
|
||||
pub const NUM_CHANNELS: usize = channel_indices::GP.end;
|
||||
|
||||
/// Calculates `lv.stack_len_bounds_aux`. Note that this must be run after decode.
|
||||
pub fn generate<F: PrimeField64>(lv: &mut CpuColumnsView<F>) {
|
||||
let cycle_filter = lv.is_cpu_cycle;
|
||||
if cycle_filter == F::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(lv.is_kernel_mode.to_canonical_u64() <= 1);
|
||||
|
||||
// Set `lv.code_context` to 0 if in kernel mode and to `lv.context` if in user mode.
|
||||
lv.code_context = (F::ONE - lv.is_kernel_mode) * lv.context;
|
||||
}
|
||||
|
||||
pub fn eval_packed<P: PackedField>(
|
||||
lv: &CpuColumnsView<P>,
|
||||
yield_constr: &mut ConstraintConsumer<P>,
|
||||
) {
|
||||
// Validate `lv.code_context`. It should be 0 if in kernel mode and `lv.context` if in user
|
||||
// mode.
|
||||
yield_constr.constraint(
|
||||
lv.is_cpu_cycle * (lv.code_context - (P::ONES - lv.is_kernel_mode) * lv.context),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||
) {
|
||||
// Validate `lv.code_context`. It should be 0 if in kernel mode and `lv.context` if in user
|
||||
// mode.
|
||||
let diff = builder.sub_extension(lv.context, lv.code_context);
|
||||
let constr = builder.mul_sub_extension(lv.is_kernel_mode, lv.context, diff);
|
||||
let filtered_constr = builder.mul_extension(lv.is_cpu_cycle, constr);
|
||||
yield_constr.constraint(builder, filtered_constr);
|
||||
}
|
||||
@ -5,6 +5,7 @@ pub mod cpu_stark;
|
||||
pub(crate) mod decode;
|
||||
mod jumps;
|
||||
pub mod kernel;
|
||||
pub(crate) mod membus;
|
||||
mod simple_logic;
|
||||
mod stack_bounds;
|
||||
mod syscalls;
|
||||
|
||||
@ -3,5 +3,5 @@ pub mod memory_stark;
|
||||
pub mod segments;
|
||||
|
||||
// TODO: Move to CPU module, now that channels have been removed from the memory table.
|
||||
pub(crate) const NUM_CHANNELS: usize = 4;
|
||||
pub(crate) const NUM_CHANNELS: usize = crate::cpu::membus::NUM_CHANNELS;
|
||||
pub(crate) const VALUE_LIMBS: usize = 8;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user