plonky2/evm/src/witness/operation.rs

733 lines
24 KiB
Rust

use ethereum_types::{BigEndianHash, U256};
use itertools::Itertools;
use keccak_hash::keccak;
use plonky2::field::types::Field;
use crate::cpu::columns::CpuColumnsView;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::assembler::BYTES_PER_OFFSET;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
use crate::cpu::membus::NUM_GP_CHANNELS;
use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff;
use crate::cpu::stack_bounds::MAX_USER_STACK_SIZE;
use crate::generation::state::GenerationState;
use crate::memory::segments::Segment;
use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge};
use crate::witness::errors::ProgramError;
use crate::witness::errors::ProgramError::MemoryError;
use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind};
use crate::witness::util::{
keccak_sponge_log, mem_read_gp_with_log_and_fill, mem_write_gp_log_and_fill,
stack_pop_with_log_and_fill, stack_push_log_and_fill,
};
use crate::{arithmetic, logic};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Operation {
Iszero,
Not,
Shl,
Shr,
Syscall(u8, usize, bool),
Eq,
BinaryLogic(logic::Op),
BinaryArithmetic(arithmetic::BinaryOperator),
TernaryArithmetic(arithmetic::TernaryOperator),
KeccakGeneral,
ProverInput,
Pop,
Jump,
Jumpi,
Pc,
Jumpdest,
Push(u8),
Dup(u8),
Swap(u8),
GetContext,
SetContext,
ExitKernel,
MloadGeneral,
MstoreGeneral,
}
pub(crate) fn generate_binary_logic_op<F: Field>(
op: logic::Op,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(in0, log_in0), (in1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let operation = logic::Operation::new(op, in0, in1);
let log_out = stack_push_log_and_fill(state, &mut row, operation.result)?;
state.traces.push_logic(operation);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_binary_arithmetic_op<F: Field>(
operator: arithmetic::BinaryOperator,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(input0, log_in0), (input1, log_in1)] =
stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let operation = arithmetic::Operation::binary(operator, input0, input1);
let log_out = stack_push_log_and_fill(state, &mut row, operation.result())?;
state.traces.push_arithmetic(operation);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_ternary_arithmetic_op<F: Field>(
operator: arithmetic::TernaryOperator,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(input0, log_in0), (input1, log_in1), (input2, log_in2)] =
stack_pop_with_log_and_fill::<3, _>(state, &mut row)?;
let operation = arithmetic::Operation::ternary(operator, input0, input1, input2);
let log_out = stack_push_log_and_fill(state, &mut row, operation.result())?;
state.traces.push_arithmetic(operation);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_keccak_general<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
row.is_keccak_sponge = F::ONE;
let [(context, log_in0), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] =
stack_pop_with_log_and_fill::<4, _>(state, &mut row)?;
let len = len.as_usize();
let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?;
let input = (0..len)
.map(|i| {
let address = MemoryAddress {
virt: base_address.virt.saturating_add(i),
..base_address
};
let val = state.memory.get(address);
val.as_u32() as u8
})
.collect_vec();
log::debug!("Hashing {:?}", input);
let hash = keccak(&input);
let val_u64s: [u64; 4] =
core::array::from_fn(|i| u64::from_le_bytes(core::array::from_fn(|j| hash.0[i * 8 + j])));
let hash_int = U256(val_u64s);
let mut log_push = stack_push_log_and_fill(state, &mut row, hash_int)?;
log_push.value = hash.into_uint();
keccak_sponge_log(state, base_address, input);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_in3);
state.traces.push_memory(log_push);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_prover_input<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let pc = state.registers.program_counter;
let input_fn = &KERNEL.prover_inputs[&pc];
let input = state.prover_input(input_fn);
let write = stack_push_log_and_fill(state, &mut row, input)?;
state.traces.push_memory(write);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_pop<F: Field>(
state: &mut GenerationState<F>,
row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
if state.registers.stack_len == 0 {
return Err(ProgramError::StackUnderflow);
}
state.registers.stack_len -= 1;
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_jump<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(dst, log_in0)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let dst: u32 = dst
.try_into()
.map_err(|_| ProgramError::InvalidJumpDestination)?;
let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill(
NUM_GP_CHANNELS - 1,
MemoryAddress::new(state.registers.context, Segment::JumpdestBits, dst as usize),
state,
&mut row,
);
if state.registers.is_kernel {
// Don't actually do the read, just set the address, etc.
let channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1];
channel.used = F::ZERO;
channel.value[0] = F::ONE;
row.mem_channels[1].value[0] = F::ONE;
} else {
if jumpdest_bit != U256::one() {
return Err(ProgramError::InvalidJumpDestination);
}
state.traces.push_memory(jumpdest_bit_log);
}
// Extra fields required by the constraints.
row.general.jumps_mut().should_jump = F::ONE;
row.general.jumps_mut().cond_sum_pinv = F::ONE;
state.traces.push_memory(log_in0);
state.traces.push_cpu(row);
state.jump_to(dst as usize);
Ok(())
}
pub(crate) fn generate_jumpi<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(dst, log_in0), (cond, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let should_jump = !cond.is_zero();
if should_jump {
row.general.jumps_mut().should_jump = F::ONE;
let cond_sum_u64 = cond
.0
.into_iter()
.map(|limb| ((limb as u32) as u64) + (limb >> 32))
.sum();
let cond_sum = F::from_canonical_u64(cond_sum_u64);
row.general.jumps_mut().cond_sum_pinv = cond_sum.inverse();
let dst: u32 = dst
.try_into()
.map_err(|_| ProgramError::InvalidJumpiDestination)?;
state.jump_to(dst as usize);
} else {
row.general.jumps_mut().should_jump = F::ZERO;
row.general.jumps_mut().cond_sum_pinv = F::ZERO;
state.registers.program_counter += 1;
}
let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill(
NUM_GP_CHANNELS - 1,
MemoryAddress::new(
state.registers.context,
Segment::JumpdestBits,
dst.low_u32() as usize,
),
state,
&mut row,
);
if !should_jump || state.registers.is_kernel {
// Don't actually do the read, just set the address, etc.
let channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1];
channel.used = F::ZERO;
channel.value[0] = F::ONE;
} else {
if jumpdest_bit != U256::one() {
return Err(ProgramError::InvalidJumpiDestination);
}
state.traces.push_memory(jumpdest_bit_log);
}
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_pc<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let write = stack_push_log_and_fill(state, &mut row, state.registers.program_counter.into())?;
state.traces.push_memory(write);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_jumpdest<F: Field>(
state: &mut GenerationState<F>,
row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_get_context<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let ctx = state.registers.context.into();
let write = stack_push_log_and_fill(state, &mut row, ctx)?;
state.traces.push_memory(write);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_set_context<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(ctx, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let sp_to_save = state.registers.stack_len.into();
let old_ctx = state.registers.context;
let new_ctx = ctx.as_usize();
let sp_field = ContextMetadata::StackSize as usize;
let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field);
let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field);
let log_write_old_sp = mem_write_gp_log_and_fill(1, old_sp_addr, state, &mut row, sp_to_save);
let (new_sp, log_read_new_sp) = if old_ctx == new_ctx {
let op = MemoryOp::new(
MemoryChannel::GeneralPurpose(2),
state.traces.clock(),
new_sp_addr,
MemoryOpKind::Read,
sp_to_save,
);
(sp_to_save, op)
} else {
mem_read_gp_with_log_and_fill(2, new_sp_addr, state, &mut row)
};
state.registers.context = new_ctx;
state.registers.stack_len = new_sp.as_usize();
state.traces.push_memory(log_in);
state.traces.push_memory(log_write_old_sp);
state.traces.push_memory(log_read_new_sp);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_push<F: Field>(
n: u8,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let code_context = state.registers.code_context();
let num_bytes = n as usize + 1;
let initial_offset = state.registers.program_counter + 1;
// First read val without going through `mem_read_with_log` type methods, so we can pass it
// to stack_push_log_and_fill.
let bytes = (0..num_bytes)
.map(|i| {
state
.memory
.get(MemoryAddress::new(
code_context,
Segment::Code,
initial_offset + i,
))
.as_u32() as u8
})
.collect_vec();
let val = U256::from_big_endian(&bytes);
let write = stack_push_log_and_fill(state, &mut row, val)?;
state.traces.push_memory(write);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_dup<F: Field>(
n: u8,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let other_addr_lo = state
.registers
.stack_len
.checked_sub(1 + (n as usize))
.ok_or(ProgramError::StackUnderflow)?;
let other_addr = MemoryAddress::new(state.registers.context, Segment::Stack, other_addr_lo);
let (val, log_in) = mem_read_gp_with_log_and_fill(0, other_addr, state, &mut row);
let log_out = stack_push_log_and_fill(state, &mut row, val)?;
state.traces.push_memory(log_in);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_swap<F: Field>(
n: u8,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let other_addr_lo = state
.registers
.stack_len
.checked_sub(2 + (n as usize))
.ok_or(ProgramError::StackUnderflow)?;
let other_addr = MemoryAddress::new(state.registers.context, Segment::Stack, other_addr_lo);
let [(in0, log_in0)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let (in1, log_in1) = mem_read_gp_with_log_and_fill(1, other_addr, state, &mut row);
let log_out0 = mem_write_gp_log_and_fill(NUM_GP_CHANNELS - 2, other_addr, state, &mut row, in0);
let log_out1 = stack_push_log_and_fill(state, &mut row, in1)?;
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_out0);
state.traces.push_memory(log_out1);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_not<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(x, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let result = !x;
let log_out = stack_push_log_and_fill(state, &mut row, result)?;
state.traces.push_memory(log_in);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_iszero<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(x, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let is_zero = x.is_zero();
let result = {
let t: u64 = is_zero.into();
t.into()
};
let log_out = stack_push_log_and_fill(state, &mut row, result)?;
generate_pinv_diff(x, U256::zero(), &mut row);
state.traces.push_memory(log_in);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
fn append_shift<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
input0: U256,
log_in0: MemoryOp,
log_in1: MemoryOp,
result: U256,
) -> Result<(), ProgramError> {
let log_out = stack_push_log_and_fill(state, &mut row, result)?;
const LOOKUP_CHANNEL: usize = 2;
let lookup_addr = MemoryAddress::new(0, Segment::ShiftTable, input0.low_u32() as usize);
if input0.bits() <= 32 {
let (_, read) = mem_read_gp_with_log_and_fill(LOOKUP_CHANNEL, lookup_addr, state, &mut row);
state.traces.push_memory(read);
} else {
// The shift constraints still expect the address to be set, even though no read will occur.
let channel = &mut row.mem_channels[LOOKUP_CHANNEL];
channel.addr_context = F::from_canonical_usize(lookup_addr.context);
channel.addr_segment = F::from_canonical_usize(lookup_addr.segment);
channel.addr_virtual = F::from_canonical_usize(lookup_addr.virt);
}
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_shl<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(input0, log_in0), (input1, log_in1)] =
stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let result = input1 << input0;
append_shift(state, row, input0, log_in0, log_in1, result)
}
pub(crate) fn generate_shr<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(input0, log_in0), (input1, log_in1)] =
stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let result = input1 >> input0;
append_shift(state, row, input0, log_in0, log_in1, result)
}
pub(crate) fn generate_syscall<F: Field>(
opcode: u8,
stack_values_read: usize,
stack_len_increased: bool,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
if TryInto::<u32>::try_into(state.registers.gas_used).is_err() {
panic!();
}
if state.registers.stack_len < stack_values_read {
return Err(ProgramError::StackUnderflow);
}
if stack_len_increased && !state.registers.is_kernel && state.registers.stack_len >= MAX_USER_STACK_SIZE {
return Err(ProgramError::StackOverflow);
}
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"];
let handler_addr_addr =
handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize);
assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset");
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
0,
MemoryAddress::new(0, Segment::Code, handler_addr_addr),
state,
&mut row,
);
let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill(
1,
MemoryAddress::new(0, Segment::Code, handler_addr_addr + 1),
state,
&mut row,
);
let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill(
2,
MemoryAddress::new(0, Segment::Code, handler_addr_addr + 2),
state,
&mut row,
);
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
let new_program_counter = handler_addr.as_usize();
let syscall_info = U256::from(state.registers.program_counter + 1)
+ (U256::from(u64::from(state.registers.is_kernel)) << 32)
+ (U256::from(state.registers.gas_used) << 192);
let log_out = stack_push_log_and_fill(state, &mut row, syscall_info)?;
state.registers.program_counter = new_program_counter;
log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter));
state.registers.is_kernel = true;
state.registers.gas_used = 0;
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_eq<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(in0, log_in0), (in1, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
let eq = in0 == in1;
let result = U256::from(u64::from(eq));
let log_out = stack_push_log_and_fill(state, &mut row, result)?;
generate_pinv_diff(in0, in1, &mut row);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_exit_kernel<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(kexit_info, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
let kexit_info_u64 = kexit_info.0[0];
let program_counter = kexit_info_u64 as u32 as usize;
let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32;
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
let is_kernel_mode = is_kernel_mode_val != 0;
let gas_used_val = kexit_info.0[3];
if TryInto::<u32>::try_into(gas_used_val).is_err() {
panic!();
}
if is_kernel_mode {
row.general.exit_kernel_mut().stack_len_check_aux = F::ZERO;
} else {
let diff = F::from_canonical_usize(state.registers.stack_len + 1) - F::from_canonical_u32(1026);
if let Some(inv) = diff.try_inverse() {
row.general.exit_kernel_mut().stack_len_check_aux = inv;
} else {
panic!("stack overflow; should have been caught before entering the syscall")
}
}
state.registers.program_counter = program_counter;
state.registers.is_kernel = is_kernel_mode;
state.registers.gas_used = gas_used_val;
log::debug!(
"Exiting to {}, is_kernel={}",
program_counter,
is_kernel_mode
);
state.traces.push_memory(log_in);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_mload_general<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(context, log_in0), (segment, log_in1), (virt, log_in2)] =
stack_pop_with_log_and_fill::<3, _>(state, &mut row)?;
let (val, log_read) = mem_read_gp_with_log_and_fill(
3,
MemoryAddress::new_u256s(context, segment, virt)?,
state,
&mut row,
);
let log_out = stack_push_log_and_fill(state, &mut row, val)?;
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_read);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_mstore_general<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let [(context, log_in0), (segment, log_in1), (virt, log_in2), (val, log_in3)] =
stack_pop_with_log_and_fill::<4, _>(state, &mut row)?;
let address = MemoryAddress {
context: context
.try_into()
.map_err(|_| MemoryError(ContextTooLarge { context }))?,
segment: segment
.try_into()
.map_err(|_| MemoryError(SegmentTooLarge { segment }))?,
virt: virt
.try_into()
.map_err(|_| MemoryError(VirtTooLarge { virt }))?,
};
let log_write = mem_write_gp_log_and_fill(4, address, state, &mut row, val);
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_in3);
state.traces.push_memory(log_write);
state.traces.push_cpu(row);
Ok(())
}
pub(crate) fn generate_exception<F: Field>(
exc_code: u8,
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
if TryInto::<u32>::try_into(state.registers.gas_used).is_err() {
panic!();
}
row.stack_len_bounds_aux = (row.stack_len + F::ONE).inverse();
row.general.exception_mut().exc_code_bits = [
F::from_bool(exc_code & 1 != 0),
F::from_bool(exc_code & 2 != 0),
F::from_bool(exc_code & 4 != 0),
];
let handler_jumptable_addr = KERNEL.global_labels["exception_jumptable"];
let handler_addr_addr =
handler_jumptable_addr + (exc_code as usize) * (BYTES_PER_OFFSET as usize);
assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset");
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
0,
MemoryAddress::new(0, Segment::Code, handler_addr_addr),
state,
&mut row,
);
let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill(
1,
MemoryAddress::new(0, Segment::Code, handler_addr_addr + 1),
state,
&mut row,
);
let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill(
2,
MemoryAddress::new(0, Segment::Code, handler_addr_addr + 2),
state,
&mut row,
);
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
let new_program_counter = handler_addr.as_usize();
let exc_info = U256::from(state.registers.program_counter)
+ (U256::from(state.registers.gas_used) << 192);
let log_out = stack_push_log_and_fill(state, &mut row, exc_info)?;
state.registers.program_counter = new_program_counter;
log::debug!("Exception to {}", KERNEL.offset_name(new_program_counter));
state.registers.is_kernel = true;
state.registers.gas_used = 0;
state.traces.push_memory(log_in0);
state.traces.push_memory(log_in1);
state.traces.push_memory(log_in2);
state.traces.push_memory(log_out);
state.traces.push_cpu(row);
Ok(())
}