Misc work on witness generation

This commit is contained in:
Daniel Lubarov 2022-11-30 12:55:41 -08:00
parent 2d92b4b6b4
commit afb3e4b1e1
15 changed files with 577 additions and 501 deletions

View File

@ -14,17 +14,22 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer
use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS};
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::keccak_util::keccakf_u32s;
use crate::generation::state::GenerationState;
use crate::keccak_sponge::columns::KECCAK_RATE_U32S;
use crate::memory::segments::Segment;
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
use crate::witness::memory::{MemoryAddress, MemoryState};
use crate::witness::traces::Traces;
use crate::witness::util::mem_write_gp_log_and_fill;
/// We can't process more than `NUM_CHANNELS` bytes per row, since that's all the memory bandwidth
/// we have. We also can't process more than 4 bytes (or the number of bytes in a `u32`), since we
/// want them to fit in a single limb of Keccak input.
const BYTES_PER_ROW: usize = 4;
pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>) {
pub(crate) fn generate_bootstrap_kernel<F: Field>(
memory: &mut MemoryState,
traces: &mut Traces<F>,
) {
let mut sponge_state = [0u32; 50];
let mut sponge_input_pos: usize = 0;
@ -35,30 +40,41 @@ pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>
.enumerate()
.chunks(BYTES_PER_ROW)
{
state.current_cpu_row.is_bootstrap_kernel = F::ONE;
let mut current_cpu_row = CpuColumnsView::default();
current_cpu_row.is_bootstrap_kernel = F::ONE;
// Write this chunk to memory, while simultaneously packing its bytes into a u32 word.
let mut packed_bytes: u32 = 0;
for (channel, (addr, &byte)) in chunk.enumerate() {
state.set_mem_cpu_current(channel, Segment::Code, addr, byte.into());
let address = MemoryAddress::new(0, Segment::Code as usize, addr);
let write = mem_write_gp_log_and_fill(
channel,
address,
traces,
&mut current_cpu_row,
byte.into(),
);
memory.set(address, byte.into());
traces.push_memory(write);
packed_bytes = (packed_bytes << 8) | byte as u32;
}
sponge_state[sponge_input_pos] = packed_bytes;
let keccak = state.current_cpu_row.general.keccak_mut();
let keccak = current_cpu_row.general.keccak_mut();
keccak.input_limbs = sponge_state.map(F::from_canonical_u32);
state.commit_cpu_row();
sponge_input_pos = (sponge_input_pos + 1) % KECCAK_RATE_U32S;
// If we just crossed a multiple of KECCAK_RATE_LIMBS, then we've filled the Keccak input
// buffer, so it's time to absorb.
if sponge_input_pos == 0 {
state.current_cpu_row.is_keccak = F::ONE;
current_cpu_row.is_keccak = F::ONE;
keccakf_u32s(&mut sponge_state);
let keccak = state.current_cpu_row.general.keccak_mut();
let keccak = current_cpu_row.general.keccak_mut();
keccak.output_limbs = sponge_state.map(F::from_canonical_u32);
}
traces.push_cpu(current_cpu_row);
}
}

View File

@ -6,6 +6,8 @@ use std::fmt::Debug;
use std::mem::{size_of, transmute};
use std::ops::{Index, IndexMut};
use plonky2::field::types::Field;
use crate::cpu::columns::general::CpuGeneralColumnsView;
use crate::cpu::columns::ops::OpsColumnsView;
use crate::cpu::membus::NUM_GP_CHANNELS;
@ -82,6 +84,12 @@ pub struct CpuColumnsView<T: Copy> {
// `u8` is guaranteed to have a `size_of` of 1.
pub const NUM_CPU_COLUMNS: usize = size_of::<CpuColumnsView<u8>>();
impl<F: Field> Default for CpuColumnsView<F> {
fn default() -> Self {
Self::from([F::ZERO; NUM_CPU_COLUMNS])
}
}
impl<T: Copy> From<[T; NUM_CPU_COLUMNS]> for CpuColumnsView<T> {
fn from(value: [T; NUM_CPU_COLUMNS]) -> Self {
unsafe { transmute_no_compile_time_size_checks(value) }

View File

@ -1,50 +1,51 @@
use ethereum_types::U256;
use crate::memory::memory_stark::MemoryOp;
use crate::memory::segments::Segment;
#[allow(unused)] // TODO: Should be used soon.
#[derive(Debug)]
pub(crate) struct MemoryState {
/// A log of each memory operation, in the order that it occurred.
pub log: Vec<MemoryOp>,
pub contexts: Vec<MemoryContextState>,
}
impl Default for MemoryState {
fn default() -> Self {
Self {
log: vec![],
// We start with an initial context for the kernel.
contexts: vec![MemoryContextState::default()],
}
}
}
#[derive(Clone, Default, Debug)]
pub(crate) struct MemoryContextState {
/// The content of each memory segment.
pub segments: [MemorySegmentState; Segment::COUNT],
}
#[derive(Clone, Default, Debug)]
pub(crate) struct MemorySegmentState {
pub content: Vec<U256>,
}
impl MemorySegmentState {
pub(crate) fn get(&self, virtual_addr: usize) -> U256 {
self.content
.get(virtual_addr)
.copied()
.unwrap_or(U256::zero())
}
pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) {
if virtual_addr >= self.content.len() {
self.content.resize(virtual_addr + 1, U256::zero());
}
self.content[virtual_addr] = value;
}
}
// TODO: Remove?
// use ethereum_types::U256;
//
// use crate::memory::memory_stark::MemoryOp;
// use crate::memory::segments::Segment;
//
// #[allow(unused)] // TODO: Should be used soon.
// #[derive(Debug)]
// pub(crate) struct MemoryState {
// /// A log of each memory operation, in the order that it occurred.
// pub log: Vec<MemoryOp>,
//
// pub contexts: Vec<MemoryContextState>,
// }
//
// impl Default for MemoryState {
// fn default() -> Self {
// Self {
// log: vec![],
// // We start with an initial context for the kernel.
// contexts: vec![MemoryContextState::default()],
// }
// }
// }
//
// #[derive(Clone, Default, Debug)]
// pub(crate) struct MemoryContextState {
// /// The content of each memory segment.
// pub segments: [MemorySegmentState; Segment::COUNT],
// }
//
// #[derive(Clone, Default, Debug)]
// pub(crate) struct MemorySegmentState {
// pub content: Vec<U256>,
// }
//
// impl MemorySegmentState {
// pub(crate) fn get(&self, virtual_addr: usize) -> U256 {
// self.content
// .get(virtual_addr)
// .copied()
// .unwrap_or(U256::zero())
// }
//
// pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) {
// if virtual_addr >= self.content.len() {
// self.content.resize(virtual_addr + 1, U256::zero());
// }
// self.content[virtual_addr] = value;
// }
// }

View File

@ -4,7 +4,6 @@ use eth_trie_utils::partial_trie::PartialTrie;
use ethereum_types::{Address, BigEndianHash, H256};
use plonky2::field::extension::Extendable;
use plonky2::field::polynomial::PolynomialValues;
use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::util::timing::TimingTree;
use serde::{Deserialize, Serialize};
@ -12,13 +11,15 @@ use serde::{Deserialize, Serialize};
use crate::all_stark::{AllStark, NUM_TABLES};
use crate::config::StarkConfig;
use crate::cpu::bootstrap_kernel::generate_bootstrap_kernel;
use crate::cpu::columns::NUM_CPU_COLUMNS;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
use crate::generation::state::GenerationState;
use crate::memory::segments::Segment;
use crate::memory::NUM_CHANNELS;
use crate::proof::{BlockMetadata, PublicValues, TrieRoots};
use crate::util::trace_rows_to_poly_values;
use crate::witness::memory::{MemoryAddress, MemoryState};
use crate::witness::state::RegistersState;
use crate::witness::traces::Traces;
use crate::witness::transition::transition;
pub(crate) mod memory;
pub(crate) mod mpt;
@ -65,25 +66,33 @@ pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
config: &StarkConfig,
timing: &mut TimingTree,
) -> ([Vec<PolynomialValues<F>>; NUM_TABLES], PublicValues) {
let mut state = GenerationState::<F>::new(inputs.clone());
// let mut state = GenerationState::<F>::new(inputs.clone());
generate_bootstrap_kernel::<F>(&mut state);
let mut memory_state = MemoryState::default();
let mut traces = Traces::<F>::default();
generate_bootstrap_kernel::<F>(&mut memory_state, &mut traces);
for txn in &inputs.signed_txns {
generate_txn(&mut state, txn);
let mut registers_state = RegistersState::default();
let halt_pc0 = KERNEL.global_labels["halt_pc0"];
let halt_pc1 = KERNEL.global_labels["halt_pc1"];
loop {
// If we've reached the kernel's halt routine, and our trace length is a power of 2, stop.
let pc = registers_state.program_counter as usize;
let in_halt_loop = pc == halt_pc0 || pc == halt_pc1;
if in_halt_loop && traces.cpu.len().is_power_of_two() {
break;
}
registers_state = transition(registers_state, &memory_state, &mut traces);
}
// TODO: Pad to a power of two, ending in the `halt` kernel function.
let cpu_rows = state.cpu_rows.len();
let mem_end_timestamp = cpu_rows * NUM_CHANNELS;
let mut read_metadata = |field| {
state.get_mem(
let read_metadata = |field| {
memory_state.get(MemoryAddress::new(
0,
Segment::GlobalMetadata,
Segment::GlobalMetadata as usize,
field as usize,
mem_end_timestamp,
)
))
};
let trie_roots_before = TrieRoots {
@ -101,43 +110,11 @@ pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
receipts_root: H256::from_uint(&read_metadata(GlobalMetadata::ReceiptTrieRootDigestAfter)),
};
let GenerationState {
cpu_rows,
current_cpu_row,
memory,
keccak_inputs,
keccak_memory_inputs,
logic_ops,
..
} = state;
assert_eq!(current_cpu_row, [F::ZERO; NUM_CPU_COLUMNS].into());
let cpu_trace = trace_rows_to_poly_values(cpu_rows);
let keccak_trace = all_stark.keccak_stark.generate_trace(keccak_inputs, timing);
let keccak_memory_trace = all_stark.keccak_memory_stark.generate_trace(
keccak_memory_inputs,
config.fri_config.num_cap_elements(),
timing,
);
let logic_trace = all_stark.logic_stark.generate_trace(logic_ops, timing);
let memory_trace = all_stark.memory_stark.generate_trace(memory.log, timing);
let traces = [
cpu_trace,
keccak_trace,
keccak_memory_trace,
logic_trace,
memory_trace,
];
let public_values = PublicValues {
trie_roots_before,
trie_roots_after,
block_metadata: inputs.block_metadata,
};
(traces, public_values)
}
fn generate_txn<F: Field>(_state: &mut GenerationState<F>, _signed_txn: &[u8]) {
// TODO
(traces.to_tables(all_stark, config, timing), public_values)
}

View File

@ -1,35 +1,21 @@
use std::mem;
use ethereum_types::U256;
use plonky2::field::types::Field;
use tiny_keccak::keccakf;
use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS};
use crate::generation::memory::MemoryState;
use crate::generation::mpt::all_mpt_prover_inputs_reversed;
use crate::generation::rlp::all_rlp_prover_inputs_reversed;
use crate::generation::GenerationInputs;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp;
use crate::memory::memory_stark::MemoryOp;
use crate::memory::segments::Segment;
use crate::memory::NUM_CHANNELS;
use crate::util::u256_limbs;
use crate::{keccak, logic};
use crate::witness::memory::MemoryState;
use crate::witness::state::RegistersState;
use crate::witness::traces::Traces;
#[derive(Debug)]
pub(crate) struct GenerationState<F: Field> {
#[allow(unused)] // TODO: Should be used soon.
pub(crate) inputs: GenerationInputs,
pub(crate) next_txn_index: usize,
pub(crate) cpu_rows: Vec<[F; NUM_CPU_COLUMNS]>,
pub(crate) current_cpu_row: CpuColumnsView<F>,
pub(crate) current_context: usize,
pub(crate) registers: RegistersState,
pub(crate) memory: MemoryState,
pub(crate) traces: Traces<F>,
pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>,
pub(crate) keccak_memory_inputs: Vec<KeccakMemoryOp>,
pub(crate) logic_ops: Vec<logic::Operation>,
pub(crate) next_txn_index: usize,
/// Prover inputs containing MPT data, in reverse order so that the next input can be obtained
/// via `pop()`.
@ -47,206 +33,204 @@ impl<F: Field> GenerationState<F> {
Self {
inputs,
next_txn_index: 0,
cpu_rows: vec![],
current_cpu_row: [F::ZERO; NUM_CPU_COLUMNS].into(),
current_context: 0,
registers: Default::default(),
memory: MemoryState::default(),
keccak_inputs: vec![],
keccak_memory_inputs: vec![],
logic_ops: vec![],
traces: Traces::default(),
next_txn_index: 0,
mpt_prover_inputs,
rlp_prover_inputs,
}
}
/// Compute logical AND, and record the operation to be added in the logic table later.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn and(&mut self, input0: U256, input1: U256) -> U256 {
self.logic_op(logic::Op::And, input0, input1)
}
// TODO: Remove dead code below.
/// Compute logical OR, and record the operation to be added in the logic table later.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn or(&mut self, input0: U256, input1: U256) -> U256 {
self.logic_op(logic::Op::Or, input0, input1)
}
// /// Compute logical AND, and record the operation to be added in the logic table later.
// #[allow(unused)] // TODO: Should be used soon.
// pub(crate) fn and(&mut self, input0: U256, input1: U256) -> U256 {
// self.logic_op(logic::Op::And, input0, input1)
// }
/// Compute logical XOR, and record the operation to be added in the logic table later.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn xor(&mut self, input0: U256, input1: U256) -> U256 {
self.logic_op(logic::Op::Xor, input0, input1)
}
// /// Compute logical OR, and record the operation to be added in the logic table later.
// #[allow(unused)] // TODO: Should be used soon.
// pub(crate) fn or(&mut self, input0: U256, input1: U256) -> U256 {
// self.logic_op(logic::Op::Or, input0, input1)
// }
/// Compute logical AND, and record the operation to be added in the logic table later.
pub(crate) fn logic_op(&mut self, op: logic::Op, input0: U256, input1: U256) -> U256 {
let operation = logic::Operation::new(op, input0, input1);
let result = operation.result;
self.logic_ops.push(operation);
result
}
// /// Compute logical XOR, and record the operation to be added in the logic table later.
// #[allow(unused)] // TODO: Should be used soon.
// pub(crate) fn xor(&mut self, input0: U256, input1: U256) -> U256 {
// self.logic_op(logic::Op::Xor, input0, input1)
// }
/// Like `get_mem_cpu`, but reads from the current context specifically.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn get_mem_cpu_current(
&mut self,
channel_index: usize,
segment: Segment,
virt: usize,
) -> U256 {
let context = self.current_context;
self.get_mem_cpu(channel_index, context, segment, virt)
}
// /// Compute logical AND, and record the operation to be added in the logic table later.
// pub(crate) fn logic_op(&mut self, op: logic::Op, input0: U256, input1: U256) -> U256 {
// let operation = logic::Operation::new(op, input0, input1);
// let result = operation.result;
// self.logic_ops.push(operation);
// result
// }
/// Simulates the CPU reading some memory through the given channel. Besides logging the memory
/// operation, this also generates the associated registers in the current CPU row.
pub(crate) fn get_mem_cpu(
&mut self,
channel_index: usize,
context: usize,
segment: Segment,
virt: usize,
) -> U256 {
let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index;
let value = self.get_mem(context, segment, virt, timestamp);
// /// Like `get_mem_cpu`, but reads from the current context specifically.
// #[allow(unused)] // TODO: Should be used soon.
// pub(crate) fn get_mem_cpu_current(
// &mut self,
// channel_index: usize,
// segment: Segment,
// virt: usize,
// ) -> U256 {
// let context = self.current_context;
// self.get_mem_cpu(channel_index, context, segment, virt)
// }
let channel = &mut self.current_cpu_row.mem_channels[channel_index];
channel.used = F::ONE;
channel.is_read = F::ONE;
channel.addr_context = F::from_canonical_usize(context);
channel.addr_segment = F::from_canonical_usize(segment as usize);
channel.addr_virtual = F::from_canonical_usize(virt);
channel.value = u256_limbs(value);
// /// Simulates the CPU reading some memory through the given channel. Besides logging the memory
// /// operation, this also generates the associated registers in the current CPU row.
// pub(crate) fn get_mem_cpu(
// &mut self,
// channel_index: usize,
// context: usize,
// segment: Segment,
// virt: usize,
// ) -> U256 {
// let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index;
// let value = self.get_mem(context, segment, virt, timestamp);
//
// let channel = &mut self.current_cpu_row.mem_channels[channel_index];
// channel.used = F::ONE;
// channel.is_read = F::ONE;
// channel.addr_context = F::from_canonical_usize(context);
// channel.addr_segment = F::from_canonical_usize(segment as usize);
// channel.addr_virtual = F::from_canonical_usize(virt);
// channel.value = u256_limbs(value);
//
// value
// }
value
}
// /// Read some memory, and log the operation.
// pub(crate) fn get_mem(
// &mut self,
// context: usize,
// segment: Segment,
// virt: usize,
// timestamp: usize,
// ) -> U256 {
// let value = self.memory.contexts[context].segments[segment as usize].get(virt);
// self.memory.log.push(MemoryOp {
// filter: true,
// timestamp,
// is_read: true,
// context,
// segment,
// virt,
// value,
// });
// value
// }
/// Read some memory, and log the operation.
pub(crate) fn get_mem(
&mut self,
context: usize,
segment: Segment,
virt: usize,
timestamp: usize,
) -> U256 {
let value = self.memory.contexts[context].segments[segment as usize].get(virt);
self.memory.log.push(MemoryOp {
filter: true,
timestamp,
is_read: true,
context,
segment,
virt,
value,
});
value
}
// /// Write some memory within the current execution context, and log the operation.
// pub(crate) fn set_mem_cpu_current(
// &mut self,
// channel_index: usize,
// segment: Segment,
// virt: usize,
// value: U256,
// ) {
// let context = self.current_context;
// self.set_mem_cpu(channel_index, context, segment, virt, value);
// }
/// Write some memory within the current execution context, and log the operation.
pub(crate) fn set_mem_cpu_current(
&mut self,
channel_index: usize,
segment: Segment,
virt: usize,
value: U256,
) {
let context = self.current_context;
self.set_mem_cpu(channel_index, context, segment, virt, value);
}
// /// Write some memory, and log the operation.
// pub(crate) fn set_mem_cpu(
// &mut self,
// channel_index: usize,
// context: usize,
// segment: Segment,
// virt: usize,
// value: U256,
// ) {
// let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index;
// self.set_mem(context, segment, virt, value, timestamp);
//
// let channel = &mut self.current_cpu_row.mem_channels[channel_index];
// channel.used = F::ONE;
// channel.is_read = F::ZERO; // For clarity; should already be 0.
// channel.addr_context = F::from_canonical_usize(context);
// channel.addr_segment = F::from_canonical_usize(segment as usize);
// channel.addr_virtual = F::from_canonical_usize(virt);
// channel.value = u256_limbs(value);
// }
/// Write some memory, and log the operation.
pub(crate) fn set_mem_cpu(
&mut self,
channel_index: usize,
context: usize,
segment: Segment,
virt: usize,
value: U256,
) {
let timestamp = self.cpu_rows.len() * NUM_CHANNELS + channel_index;
self.set_mem(context, segment, virt, value, timestamp);
// /// Write some memory, and log the operation.
// pub(crate) fn set_mem(
// &mut self,
// context: usize,
// segment: Segment,
// virt: usize,
// value: U256,
// timestamp: usize,
// ) {
// self.memory.log.push(MemoryOp {
// filter: true,
// timestamp,
// is_read: false,
// context,
// segment,
// virt,
// value,
// });
// self.memory.contexts[context].segments[segment as usize].set(virt, value)
// }
let channel = &mut self.current_cpu_row.mem_channels[channel_index];
channel.used = F::ONE;
channel.is_read = F::ZERO; // For clarity; should already be 0.
channel.addr_context = F::from_canonical_usize(context);
channel.addr_segment = F::from_canonical_usize(segment as usize);
channel.addr_virtual = F::from_canonical_usize(virt);
channel.value = u256_limbs(value);
}
// /// Evaluate the Keccak-f permutation in-place on some data in memory, and record the operations
// /// for the purpose of witness generation.
// #[allow(unused)] // TODO: Should be used soon.
// pub(crate) fn keccak_memory(
// &mut self,
// context: usize,
// segment: Segment,
// virt: usize,
// ) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
// let read_timestamp = self.cpu_rows.len() * NUM_CHANNELS;
// let _write_timestamp = read_timestamp + 1;
// let input = (0..25)
// .map(|i| {
// let bytes = [0, 1, 2, 3, 4, 5, 6, 7].map(|j| {
// let virt = virt + i * 8 + j;
// let byte = self.get_mem(context, segment, virt, read_timestamp);
// debug_assert!(byte.bits() <= 8);
// byte.as_u32() as u8
// });
// u64::from_le_bytes(bytes)
// })
// .collect::<Vec<_>>()
// .try_into()
// .unwrap();
// let output = self.keccak(input);
// self.keccak_memory_inputs.push(KeccakMemoryOp {
// context,
// segment,
// virt,
// read_timestamp,
// input,
// output,
// });
// // TODO: Write output to memory.
// output
// }
/// Write some memory, and log the operation.
pub(crate) fn set_mem(
&mut self,
context: usize,
segment: Segment,
virt: usize,
value: U256,
timestamp: usize,
) {
self.memory.log.push(MemoryOp {
filter: true,
timestamp,
is_read: false,
context,
segment,
virt,
value,
});
self.memory.contexts[context].segments[segment as usize].set(virt, value)
}
// /// Evaluate the Keccak-f permutation, and record the operation for the purpose of witness
// /// generation.
// pub(crate) fn keccak(
// &mut self,
// mut input: [u64; keccak::keccak_stark::NUM_INPUTS],
// ) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
// self.keccak_inputs.push(input);
// keccakf(&mut input);
// input
// }
/// Evaluate the Keccak-f permutation in-place on some data in memory, and record the operations
/// for the purpose of witness generation.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn keccak_memory(
&mut self,
context: usize,
segment: Segment,
virt: usize,
) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
let read_timestamp = self.cpu_rows.len() * NUM_CHANNELS;
let _write_timestamp = read_timestamp + 1;
let input = (0..25)
.map(|i| {
let bytes = [0, 1, 2, 3, 4, 5, 6, 7].map(|j| {
let virt = virt + i * 8 + j;
let byte = self.get_mem(context, segment, virt, read_timestamp);
debug_assert!(byte.bits() <= 8);
byte.as_u32() as u8
});
u64::from_le_bytes(bytes)
})
.collect::<Vec<_>>()
.try_into()
.unwrap();
let output = self.keccak(input);
self.keccak_memory_inputs.push(KeccakMemoryOp {
context,
segment,
virt,
read_timestamp,
input,
output,
});
// TODO: Write output to memory.
output
}
/// Evaluate the Keccak-f permutation, and record the operation for the purpose of witness
/// generation.
pub(crate) fn keccak(
&mut self,
mut input: [u64; keccak::keccak_stark::NUM_INPUTS],
) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
self.keccak_inputs.push(input);
keccakf(&mut input);
input
}
pub(crate) fn commit_cpu_row(&mut self) {
let mut swapped_row = [F::ZERO; NUM_CPU_COLUMNS].into();
mem::swap(&mut self.current_cpu_row, &mut swapped_row);
self.cpu_rows.push(swapped_row.into());
}
// pub(crate) fn commit_cpu_row(&mut self) {
// let mut swapped_row = [F::ZERO; NUM_CPU_COLUMNS].into();
// mem::swap(&mut self.current_cpu_row, &mut swapped_row);
// self.cpu_rows.push(swapped_row.into());
// }
}

View File

@ -72,13 +72,23 @@ pub struct LogicStark<F, const D: usize> {
pub f: PhantomData<F>,
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum Op {
And,
Or,
Xor,
}
impl Op {
pub(crate) fn result(&self, a: U256, b: U256) -> U256 {
match self {
Op::And => a & b,
Op::Or => a | b,
Op::Xor => a ^ b,
}
}
}
#[derive(Debug)]
pub(crate) struct Operation {
operator: Op,
@ -89,11 +99,7 @@ pub(crate) struct Operation {
impl Operation {
pub(crate) fn new(operator: Op, input0: U256, input1: U256) -> Self {
let result = match operator {
Op::And => input0 & input1,
Op::Or => input0 | input1,
Op::Xor => input0 ^ input1,
};
let result = operator.result(input0, input1);
Operation {
operator,
input0,
@ -101,6 +107,31 @@ impl Operation {
result,
}
}
fn to_row<F: Field>(&self) -> [F; NUM_COLUMNS] {
let Operation {
operator,
input0,
input1,
result,
} = self;
let mut row = [F::ZERO; NUM_COLUMNS];
row[match operator {
Op::And => columns::IS_AND,
Op::Or => columns::IS_OR,
Op::Xor => columns::IS_XOR,
}] = F::ONE;
for i in 0..256 {
row[columns::INPUT0.start + i] = F::from_bool(input0.bit(i));
row[columns::INPUT1.start + i] = F::from_bool(input1.bit(i));
}
let result_limbs: &[u64] = result.as_ref();
for (i, &limb) in result_limbs.iter().enumerate() {
row[columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32);
row[columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32);
}
row
}
}
impl<F: RichField, const D: usize> LogicStark<F, D> {
@ -128,40 +159,20 @@ impl<F: RichField, const D: usize> LogicStark<F, D> {
let mut rows = Vec::with_capacity(padded_len);
for op in operations {
rows.push(Self::generate_row(op));
rows.push(op.to_row());
}
// Pad to a power of two.
for _ in len..padded_len {
rows.push([F::ZERO; columns::NUM_COLUMNS]);
rows.push([F::ZERO; NUM_COLUMNS]);
}
rows
}
fn generate_row(operation: Operation) -> [F; columns::NUM_COLUMNS] {
let mut row = [F::ZERO; columns::NUM_COLUMNS];
match operation.operator {
Op::And => row[columns::IS_AND] = F::ONE,
Op::Or => row[columns::IS_OR] = F::ONE,
Op::Xor => row[columns::IS_XOR] = F::ONE,
}
for (i, col) in columns::INPUT0.enumerate() {
row[col] = F::from_bool(operation.input0.bit(i));
}
for (i, col) in columns::INPUT1.enumerate() {
row[col] = F::from_bool(operation.input1.bit(i));
}
for (i, col) in columns::RESULT.enumerate() {
let bit_range = i * PACKED_LIMB_BITS..(i + 1) * PACKED_LIMB_BITS;
row[col] = limb_from_bits_le(bit_range.map(|j| F::from_bool(operation.result.bit(j))));
}
row
}
}
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F, D> {
const COLUMNS: usize = columns::NUM_COLUMNS;
const COLUMNS: usize = NUM_COLUMNS;
fn eval_packed_generic<FE, P, const D2: usize>(
&self,

View File

@ -1,6 +1,5 @@
use std::marker::PhantomData;
use ethereum_types::U256;
use itertools::Itertools;
use maybe_rayon::*;
use plonky2::field::extension::{Extendable, FieldExtension};
@ -20,11 +19,12 @@ use crate::memory::columns::{
COUNTER_PERMUTED, FILTER, IS_READ, NUM_COLUMNS, RANGE_CHECK, RANGE_CHECK_PERMUTED,
SEGMENT_FIRST_CHANGE, TIMESTAMP, VIRTUAL_FIRST_CHANGE,
};
use crate::memory::segments::Segment;
use crate::memory::VALUE_LIMBS;
use crate::permutation::PermutationPair;
use crate::stark::Stark;
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
use crate::witness::memory::MemoryOpKind::Read;
use crate::witness::memory::{MemoryAddress, MemoryOp};
pub fn ctl_data<F: Field>() -> Vec<Column<F>> {
let mut res =
@ -43,17 +43,18 @@ pub struct MemoryStark<F, const D: usize> {
pub(crate) f: PhantomData<F>,
}
#[derive(Clone, Debug)]
pub(crate) struct MemoryOp {
/// true if this is an actual memory operation, or false if it's a padding row.
pub filter: bool,
pub timestamp: usize,
pub is_read: bool,
pub context: usize,
pub segment: Segment,
pub virt: usize,
pub value: U256,
}
// TODO: Remove
// #[derive(Clone, Debug)]
// pub(crate) struct MemoryOp {
// /// true if this is an actual memory operation, or false if it's a padding row.
// pub filter: bool,
// pub timestamp: usize,
// pub is_read: bool,
// pub context: usize,
// pub segment: Segment,
// pub virt: usize,
// pub value: U256,
// }
impl MemoryOp {
/// Generate a row for a given memory operation. Note that this does not generate columns which
@ -64,10 +65,15 @@ impl MemoryOp {
let mut row = [F::ZERO; NUM_COLUMNS];
row[FILTER] = F::from_bool(self.filter);
row[TIMESTAMP] = F::from_canonical_usize(self.timestamp);
row[IS_READ] = F::from_bool(self.is_read);
row[ADDR_CONTEXT] = F::from_canonical_usize(self.context);
row[ADDR_SEGMENT] = F::from_canonical_usize(self.segment as usize);
row[ADDR_VIRTUAL] = F::from_canonical_usize(self.virt);
row[IS_READ] = F::from_bool(self.op == Read);
let MemoryAddress {
context,
segment,
virt,
} = self.address;
row[ADDR_CONTEXT] = F::from_canonical_usize(context);
row[ADDR_SEGMENT] = F::from_canonical_usize(segment);
row[ADDR_VIRTUAL] = F::from_canonical_usize(virt);
for j in 0..VALUE_LIMBS {
row[value_limb(j)] = F::from_canonical_u32((self.value >> (j * 32)).low_u32());
}
@ -80,12 +86,12 @@ fn get_max_range_check(memory_ops: &[MemoryOp]) -> usize {
.iter()
.tuple_windows()
.map(|(curr, next)| {
if curr.context != next.context {
next.context - curr.context - 1
} else if curr.segment != next.segment {
next.segment as usize - curr.segment as usize - 1
} else if curr.virt != next.virt {
next.virt - curr.virt - 1
if curr.address.context != next.address.context {
next.address.context - curr.address.context - 1
} else if curr.address.segment != next.address.segment {
next.address.segment as usize - curr.address.segment as usize - 1
} else if curr.address.virt != next.address.virt {
next.address.virt - curr.address.virt - 1
} else {
next.timestamp - curr.timestamp - 1
}
@ -140,7 +146,14 @@ impl<F: RichField + Extendable<D>, const D: usize> MemoryStark<F, D> {
/// Generate most of the trace rows. Excludes a few columns like `COUNTER`, which are generated
/// later, after transposing to column-major form.
fn generate_trace_row_major(&self, mut memory_ops: Vec<MemoryOp>) -> Vec<[F; NUM_COLUMNS]> {
memory_ops.sort_by_key(|op| (op.context, op.segment, op.virt, op.timestamp));
memory_ops.sort_by_key(|op| {
(
op.address.context,
op.address.segment,
op.address.virt,
op.timestamp,
)
});
Self::pad_memory_ops(&mut memory_ops);
@ -181,7 +194,7 @@ impl<F: RichField + Extendable<D>, const D: usize> MemoryStark<F, D> {
memory_ops.push(MemoryOp {
filter: false,
timestamp: last_op.timestamp + i + 1,
is_read: true,
op: Read,
..last_op
});
}

View File

@ -24,19 +24,37 @@ impl MemoryChannel {
}
}
pub type MemoryAddress = (u32, u32, u32);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct MemoryAddress {
pub(crate) context: usize,
pub(crate) segment: usize,
pub(crate) virt: usize,
}
#[derive(Clone, Copy, Debug)]
impl MemoryAddress {
pub(crate) fn new(context: usize, segment: usize, virt: usize) -> Self {
Self {
context,
segment,
virt,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MemoryOpKind {
Read,
Write(U256),
Write,
}
#[derive(Clone, Copy, Debug)]
pub struct MemoryOp {
pub timestamp: u64,
/// true if this is an actual memory operation, or false if it's a padding row.
pub filter: bool,
pub timestamp: usize,
pub address: MemoryAddress,
pub op: MemoryOpKind,
pub value: U256,
}
impl MemoryOp {
@ -45,17 +63,20 @@ impl MemoryOp {
clock: usize,
address: MemoryAddress,
op: MemoryOpKind,
value: U256,
) -> Self {
let timestamp = (clock * NUM_CHANNELS + channel.index()) as u64;
let timestamp = clock * NUM_CHANNELS + channel.index();
MemoryOp {
filter: true,
timestamp,
address,
op,
value,
}
}
}
#[derive(Clone)]
#[derive(Clone, Default, Debug)]
pub struct MemoryState {
contents: HashMap<MemoryAddress, U256>,
}
@ -66,7 +87,7 @@ impl MemoryState {
for (i, &byte) in kernel_code.iter().enumerate() {
if byte != 0 {
let address = (0, 0, i as u32);
let address = MemoryAddress::new(0, 0, i);
let val = byte.into();
contents.insert(address, val);
}

View File

@ -1,7 +1,7 @@
mod errors;
mod memory;
pub(crate) mod memory;
mod operation;
mod state;
mod traces;
pub(crate) mod state;
pub(crate) mod traces;
pub mod transition;
mod util;
pub(crate) mod util;

View File

@ -8,7 +8,7 @@ use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff;
use crate::logic;
use crate::memory::segments::Segment;
use crate::witness::errors::ProgramError;
use crate::witness::memory::MemoryState;
use crate::witness::memory::{MemoryAddress, MemoryState};
use crate::witness::state::RegistersState;
use crate::witness::traces::Traces;
use crate::witness::util::{
@ -17,7 +17,7 @@ use crate::witness::util::{
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Operation {
pub(crate) enum Operation {
Dup(u8),
Swap(u8),
Iszero,
@ -25,53 +25,12 @@ pub enum Operation {
Syscall(u8),
Eq,
ExitKernel,
BinaryLogic(BinaryLogicOp),
BinaryLogic(logic::Op),
NotImplemented,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BinaryLogicOp {
And,
Or,
Xor,
}
impl BinaryLogicOp {
pub(self) fn result(&self, a: U256, b: U256) -> U256 {
match self {
BinaryLogicOp::And => a & b,
BinaryLogicOp::Or => a | b,
BinaryLogicOp::Xor => a ^ b,
}
}
}
fn make_logic_row<F: Field>(
op: BinaryLogicOp,
in0: U256,
in1: U256,
result: U256,
) -> [F; logic::columns::NUM_COLUMNS] {
let mut row = [F::ZERO; logic::columns::NUM_COLUMNS];
row[match op {
BinaryLogicOp::And => logic::columns::IS_AND,
BinaryLogicOp::Or => logic::columns::IS_OR,
BinaryLogicOp::Xor => logic::columns::IS_XOR,
}] = F::ONE;
for i in 0..256 {
row[logic::columns::INPUT0.start + i] = F::from_bool(in0.bit(i));
row[logic::columns::INPUT1.start + i] = F::from_bool(in1.bit(i));
}
let result_limbs: &[u64] = result.as_ref();
for (i, &limb) in result_limbs.iter().enumerate() {
row[logic::columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32);
row[logic::columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32);
}
row
}
pub fn generate_binary_logic_op<F: Field>(
op: BinaryLogicOp,
pub(crate) fn generate_binary_logic_op<F: Field>(
op: logic::Op,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
@ -82,7 +41,7 @@ pub fn generate_binary_logic_op<F: Field>(
let result = op.result(in0, in1);
let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?;
traces.push_logic(make_logic_row(op, in0, in1, result));
traces.push_logic(logic::Operation::new(op, in0, in1));
traces.push_memory(log_in0);
traces.push_memory(log_in1);
traces.push_memory(log_out);
@ -90,7 +49,7 @@ pub fn generate_binary_logic_op<F: Field>(
Ok(registers_state)
}
pub fn generate_dup<F: Field>(
pub(crate) fn generate_dup<F: Field>(
n: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
@ -99,11 +58,11 @@ pub fn generate_dup<F: Field>(
) -> Result<RegistersState, ProgramError> {
let other_addr_lo = registers_state
.stack_len
.checked_sub(1 + (n as u32))
.checked_sub(1 + (n as usize))
.ok_or(ProgramError::StackUnderflow)?;
let other_addr = (
let other_addr = MemoryAddress::new(
registers_state.context,
Segment::Stack as u32,
Segment::Stack as usize,
other_addr_lo,
);
@ -117,7 +76,7 @@ pub fn generate_dup<F: Field>(
Ok(registers_state)
}
pub fn generate_swap<F: Field>(
pub(crate) fn generate_swap<F: Field>(
n: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
@ -126,11 +85,11 @@ pub fn generate_swap<F: Field>(
) -> Result<RegistersState, ProgramError> {
let other_addr_lo = registers_state
.stack_len
.checked_sub(2 + (n as u32))
.checked_sub(2 + (n as usize))
.ok_or(ProgramError::StackUnderflow)?;
let other_addr = (
let other_addr = MemoryAddress::new(
registers_state.context,
Segment::Stack as u32,
Segment::Stack as usize,
other_addr_lo,
);
@ -150,7 +109,7 @@ pub fn generate_swap<F: Field>(
Ok(registers_state)
}
pub fn generate_not<F: Field>(
pub(crate) fn generate_not<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
@ -167,7 +126,7 @@ pub fn generate_not<F: Field>(
Ok(registers_state)
}
pub fn generate_iszero<F: Field>(
pub(crate) fn generate_iszero<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
@ -190,39 +149,39 @@ pub fn generate_iszero<F: Field>(
Ok(registers_state)
}
pub fn generate_syscall<F: Field>(
pub(crate) fn generate_syscall<F: Field>(
opcode: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"] as u32;
let handler_addr_addr = handler_jumptable_addr + (opcode as u32);
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"] as usize;
let handler_addr_addr = handler_jumptable_addr + (opcode as usize);
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
0,
(0, Segment::Code as u32, handler_addr_addr),
MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr),
memory_state,
traces,
&mut row,
);
let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill(
1,
(0, Segment::Code as u32, handler_addr_addr + 1),
MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr + 1),
memory_state,
traces,
&mut row,
);
let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill(
2,
(0, Segment::Code as u32, handler_addr_addr + 2),
MemoryAddress::new(0, Segment::Code as usize, handler_addr_addr + 2),
memory_state,
traces,
&mut row,
);
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
let new_program_counter = handler_addr.as_u32();
let new_program_counter = handler_addr.as_usize();
let syscall_info = U256::from(registers_state.program_counter)
+ (U256::from(u64::from(registers_state.is_kernel)) << 32);
@ -240,7 +199,7 @@ pub fn generate_syscall<F: Field>(
Ok(registers_state)
}
pub fn generate_eq<F: Field>(
pub(crate) fn generate_eq<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
@ -261,7 +220,7 @@ pub fn generate_eq<F: Field>(
Ok(registers_state)
}
pub fn generate_exit_kernel<F: Field>(
pub(crate) fn generate_exit_kernel<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
@ -270,7 +229,7 @@ pub fn generate_exit_kernel<F: Field>(
let [(kexit_info, log_in)] =
stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?;
let kexit_info_u64: [u64; 4] = kexit_info.0;
let program_counter = kexit_info_u64[0] as u32;
let program_counter = kexit_info_u64[0] as usize;
let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32;
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
let is_kernel_mode = is_kernel_mode_val != 0;

View File

@ -1,7 +1,20 @@
use crate::cpu::kernel::aggregator::KERNEL;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegistersState {
pub program_counter: u32,
pub program_counter: usize,
pub is_kernel: bool,
pub stack_len: u32,
pub context: u32,
pub stack_len: usize,
pub context: usize,
}
impl Default for RegistersState {
fn default() -> Self {
Self {
program_counter: KERNEL.global_labels["main"],
is_kernel: true,
stack_len: 0,
context: 0,
}
}
}

View File

@ -1,9 +1,19 @@
use crate::arithmetic::columns::NUM_ARITH_COLUMNS;
use crate::cpu::columns::CpuColumnsView;
use crate::logic;
use crate::witness::memory::MemoryOp;
use plonky2::field::extension::Extendable;
use plonky2::field::polynomial::PolynomialValues;
use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::util::timing::TimingTree;
use crate::all_stark::{AllStark, NUM_TABLES};
use crate::arithmetic::columns::NUM_ARITH_COLUMNS;
use crate::config::StarkConfig;
use crate::cpu::columns::CpuColumnsView;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp;
use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp;
use crate::util::trace_rows_to_poly_values;
use crate::witness::memory::MemoryOp;
use crate::{keccak, logic};
type LogicRow<T> = [T; logic::columns::NUM_COLUMNS];
type ArithmeticRow<T> = [T; NUM_ARITH_COLUMNS];
#[derive(Clone, Copy, Debug)]
@ -14,46 +24,54 @@ pub struct TraceCheckpoint {
pub(self) memory_len: usize,
}
#[derive(Clone, Debug)]
pub struct Traces<T: Copy> {
cpu: Vec<CpuColumnsView<T>>,
logic: Vec<LogicRow<T>>,
arithmetic: Vec<ArithmeticRow<T>>,
memory: Vec<MemoryOp>,
#[derive(Debug)]
pub(crate) struct Traces<T: Copy> {
pub(crate) cpu: Vec<CpuColumnsView<T>>,
pub(crate) logic_ops: Vec<logic::Operation>,
pub(crate) arithmetic: Vec<ArithmeticRow<T>>,
pub(crate) memory_ops: Vec<MemoryOp>,
pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>,
pub(crate) keccak_memory_inputs: Vec<KeccakMemoryOp>,
pub(crate) keccak_sponge_ops: Vec<KeccakSpongeOp>,
}
impl<T: Copy> Traces<T> {
pub fn new() -> Self {
Traces {
cpu: vec![],
logic: vec![],
logic_ops: vec![],
arithmetic: vec![],
memory: vec![],
memory_ops: vec![],
keccak_inputs: vec![],
keccak_memory_inputs: vec![],
keccak_sponge_ops: vec![],
}
}
pub fn checkpoint(&self) -> TraceCheckpoint {
TraceCheckpoint {
cpu_len: self.cpu.len(),
logic_len: self.logic.len(),
logic_len: self.logic_ops.len(),
arithmetic_len: self.arithmetic.len(),
memory_len: self.memory.len(),
memory_len: self.memory_ops.len(),
// TODO others
}
}
pub fn rollback(&mut self, checkpoint: TraceCheckpoint) {
self.cpu.truncate(checkpoint.cpu_len);
self.logic.truncate(checkpoint.logic_len);
self.logic_ops.truncate(checkpoint.logic_len);
self.arithmetic.truncate(checkpoint.arithmetic_len);
self.memory.truncate(checkpoint.memory_len);
self.memory_ops.truncate(checkpoint.memory_len);
// TODO others
}
pub fn push_cpu(&mut self, val: CpuColumnsView<T>) {
self.cpu.push(val);
}
pub fn push_logic(&mut self, val: LogicRow<T>) {
self.logic.push(val);
pub fn push_logic(&mut self, val: logic::Operation) {
self.logic_ops.push(val);
}
pub fn push_arithmetic(&mut self, val: ArithmeticRow<T>) {
@ -61,12 +79,51 @@ impl<T: Copy> Traces<T> {
}
pub fn push_memory(&mut self, val: MemoryOp) {
self.memory.push(val);
self.memory_ops.push(val);
}
pub fn clock(&self) -> usize {
self.cpu.len()
}
pub fn to_tables<const D: usize>(
self,
all_stark: &AllStark<T, D>,
config: &StarkConfig,
timing: &mut TimingTree,
) -> [Vec<PolynomialValues<T>>; NUM_TABLES]
where
T: RichField + Extendable<D>,
{
let Traces {
cpu,
logic_ops,
arithmetic,
memory_ops,
keccak_inputs,
keccak_memory_inputs,
keccak_sponge_ops,
} = self;
let cpu_rows = cpu.into_iter().map(|x| x.into()).collect();
let cpu_trace = trace_rows_to_poly_values(cpu_rows);
let keccak_trace = all_stark.keccak_stark.generate_trace(keccak_inputs, timing);
let keccak_memory_trace = all_stark.keccak_memory_stark.generate_trace(
keccak_memory_inputs,
config.fri_config.num_cap_elements(),
timing,
);
let logic_trace = all_stark.logic_stark.generate_trace(logic_ops, timing);
let memory_trace = all_stark.memory_stark.generate_trace(memory_ops, timing);
[
cpu_trace,
keccak_trace,
keccak_memory_trace,
logic_trace,
memory_trace,
]
}
}
impl<T: Copy> Default for Traces<T> {

View File

@ -1,18 +1,19 @@
use plonky2::field::types::Field;
use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS};
use crate::logic;
use crate::memory::segments::Segment;
use crate::witness::errors::ProgramError;
use crate::witness::memory::MemoryState;
use crate::witness::memory::{MemoryAddress, MemoryState};
use crate::witness::operation::{
generate_binary_logic_op, generate_dup, generate_eq, generate_exit_kernel, generate_iszero,
generate_not, generate_swap, generate_syscall, BinaryLogicOp, Operation,
generate_not, generate_swap, generate_syscall, Operation,
};
use crate::witness::state::RegistersState;
use crate::witness::traces::Traces;
use crate::witness::util::mem_read_code_with_log_and_fill;
const KERNEL_CONTEXT: u32 = 0;
const KERNEL_CONTEXT: usize = 0;
fn read_code_memory<F: Field>(
registers_state: RegistersState,
@ -25,11 +26,11 @@ fn read_code_memory<F: Field>(
} else {
registers_state.context
};
row.code_context = F::from_canonical_u32(code_context);
row.code_context = F::from_canonical_usize(code_context);
let address = (
let address = MemoryAddress::new(
code_context,
Segment::Code as u32,
Segment::Code as usize,
registers_state.program_counter,
);
let (opcode, mem_log) = mem_read_code_with_log_and_fill(address, memory_state, traces, row);
@ -62,9 +63,9 @@ fn decode(registers_state: RegistersState, opcode: u8) -> Result<Operation, Prog
(0x13, _) => Ok(Operation::Syscall(opcode)),
(0x14, _) => Ok(Operation::Eq),
(0x15, _) => Ok(Operation::Iszero),
(0x16, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::And)),
(0x17, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Or)),
(0x18, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Xor)),
(0x16, _) => Ok(Operation::BinaryLogic(logic::Op::And)),
(0x17, _) => Ok(Operation::BinaryLogic(logic::Op::Or)),
(0x18, _) => Ok(Operation::BinaryLogic(logic::Op::Xor)),
(0x19, _) => Ok(Operation::Not),
(0x1a, _) => Ok(Operation::NotImplemented),
(0x1b, _) => Ok(Operation::NotImplemented),
@ -151,9 +152,9 @@ fn fill_op_flag<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
Operation::Syscall(_) => &mut flags.syscall,
Operation::Eq => &mut flags.eq,
Operation::ExitKernel => &mut flags.exit_kernel,
Operation::BinaryLogic(BinaryLogicOp::And) => &mut flags.and,
Operation::BinaryLogic(BinaryLogicOp::Or) => &mut flags.or,
Operation::BinaryLogic(BinaryLogicOp::Xor) => &mut flags.xor,
Operation::BinaryLogic(logic::Op::And) => &mut flags.and,
Operation::BinaryLogic(logic::Op::Or) => &mut flags.or,
Operation::BinaryLogic(logic::Op::Xor) => &mut flags.xor,
Operation::NotImplemented => panic!("operation not implemented"),
} = F::ONE;
}
@ -199,6 +200,14 @@ fn try_perform_instruction<F: Field>(
let opcode = read_code_memory(registers_state, memory_state, traces, &mut row);
let op = decode(registers_state, opcode)?;
log::trace!(
"Executing {}={:?} at {}",
opcode,
op,
registers_state.program_counter
);
// TODO: Temporarily slowing down so we can view logs easily.
std::thread::sleep(std::time::Duration::from_millis(200));
fill_op_flag(op, &mut row);
perform_op(op, registers_state, memory_state, traces, row)
@ -212,7 +221,7 @@ fn handle_error<F: Field>(
todo!("constraints for exception handling are not implemented");
}
pub fn transition<F: Field>(
pub(crate) fn transition<F: Field>(
registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,

View File

@ -23,27 +23,27 @@ fn to_bits_le<F: Field>(n: u8) -> [F; 8] {
res
}
pub fn mem_read_with_log<T: Copy>(
pub(crate) fn mem_read_with_log<T: Copy>(
channel: MemoryChannel,
address: MemoryAddress,
memory_state: &MemoryState,
traces: &Traces<T>,
) -> (U256, MemoryOp) {
let val = memory_state.get(address);
let op = MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Read);
let op = MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Read, val);
(val, op)
}
pub fn mem_write_log<T: Copy>(
pub(crate) fn mem_write_log<T: Copy>(
channel: MemoryChannel,
address: MemoryAddress,
traces: &Traces<T>,
val: U256,
) -> MemoryOp {
MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write(val))
MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write, val)
}
pub fn mem_read_code_with_log_and_fill<F: Field>(
pub(crate) fn mem_read_code_with_log_and_fill<F: Field>(
address: MemoryAddress,
memory_state: &MemoryState,
traces: &Traces<F>,
@ -57,7 +57,7 @@ pub fn mem_read_code_with_log_and_fill<F: Field>(
(val_u8, op)
}
pub fn mem_read_gp_with_log_and_fill<F: Field>(
pub(crate) fn mem_read_gp_with_log_and_fill<F: Field>(
n: usize,
address: MemoryAddress,
memory_state: &MemoryState,
@ -75,9 +75,9 @@ pub fn mem_read_gp_with_log_and_fill<F: Field>(
let channel = &mut row.mem_channels[n];
channel.used = F::ONE;
channel.is_read = F::ONE;
channel.addr_context = F::from_canonical_u32(address.0);
channel.addr_segment = F::from_canonical_u32(address.1);
channel.addr_virtual = F::from_canonical_u32(address.2);
channel.addr_context = F::from_canonical_usize(address.context);
channel.addr_segment = F::from_canonical_usize(address.segment);
channel.addr_virtual = F::from_canonical_usize(address.virt);
for (i, limb) in val_limbs.into_iter().enumerate() {
channel.value[2 * i] = F::from_canonical_u32(limb as u32);
channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32);
@ -86,7 +86,7 @@ pub fn mem_read_gp_with_log_and_fill<F: Field>(
(val, op)
}
pub fn mem_write_gp_log_and_fill<F: Field>(
pub(crate) fn mem_write_gp_log_and_fill<F: Field>(
n: usize,
address: MemoryAddress,
traces: &Traces<F>,
@ -99,9 +99,9 @@ pub fn mem_write_gp_log_and_fill<F: Field>(
let channel = &mut row.mem_channels[n];
channel.used = F::ONE;
channel.is_read = F::ZERO;
channel.addr_context = F::from_canonical_u32(address.0);
channel.addr_segment = F::from_canonical_u32(address.1);
channel.addr_virtual = F::from_canonical_u32(address.2);
channel.addr_context = F::from_canonical_usize(address.context);
channel.addr_segment = F::from_canonical_usize(address.segment);
channel.addr_virtual = F::from_canonical_usize(address.virt);
for (i, limb) in val_limbs.into_iter().enumerate() {
channel.value[2 * i] = F::from_canonical_u32(limb as u32);
channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32);
@ -110,7 +110,7 @@ pub fn mem_write_gp_log_and_fill<F: Field>(
op
}
pub fn stack_pop_with_log_and_fill<const N: usize, F: Field>(
pub(crate) fn stack_pop_with_log_and_fill<const N: usize, F: Field>(
registers_state: &mut RegistersState,
memory_state: &MemoryState,
traces: &Traces<F>,
@ -123,10 +123,10 @@ pub fn stack_pop_with_log_and_fill<const N: usize, F: Field>(
let result = {
let mut i = 0usize;
[(); N].map(|_| {
let address = (
let address = MemoryAddress::new(
registers_state.context,
Segment::Stack as u32,
registers_state.stack_len - 1 - (i as u32),
Segment::Stack as usize,
registers_state.stack_len - 1 - i,
);
let res = mem_read_gp_with_log_and_fill(i, address, memory_state, traces, row);
i += 1;
@ -134,12 +134,12 @@ pub fn stack_pop_with_log_and_fill<const N: usize, F: Field>(
})
};
registers_state.stack_len -= N as u32;
registers_state.stack_len -= N;
Ok(result)
}
pub fn stack_push_log_and_fill<F: Field>(
pub(crate) fn stack_push_log_and_fill<F: Field>(
registers_state: &mut RegistersState,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
@ -149,9 +149,9 @@ pub fn stack_push_log_and_fill<F: Field>(
return Err(ProgramError::StackOverflow);
}
let address = (
let address = MemoryAddress::new(
registers_state.context,
Segment::Stack as u32,
Segment::Stack as usize,
registers_state.stack_len,
);
let res = mem_write_gp_log_and_fill(NUM_GP_CHANNELS - 1, address, traces, row, val);

View File

@ -1,6 +1,7 @@
use std::collections::HashMap;
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
use eth_trie_utils::partial_trie::PartialTrie;
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::plonk::config::PoseidonGoldilocksConfig;
use plonky2::util::timing::TimingTree;
@ -18,6 +19,8 @@ type C = PoseidonGoldilocksConfig;
/// Execute the empty list of transactions, i.e. a no-op.
#[test]
fn test_empty_txn_list() -> anyhow::Result<()> {
init_logger();
let all_stark = AllStark::<F, D>::default();
let config = StarkConfig::standard_fast_config();
@ -80,3 +83,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
verify_proof(all_stark, proof, &config)
}
fn init_logger() {
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "trace"));
}