mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-05 07:13:08 +00:00
EVM decode (#553)
* EVM decode * Style and docs * Daniel PR comments * Minor style * Work on tests * Tests passing! * Minor: fix comment * William comments
This commit is contained in:
parent
d6006f8fff
commit
47efff834f
@ -56,10 +56,10 @@ mod tests {
|
||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::all_stark::{AllStark, Table};
|
||||
use crate::config::StarkConfig;
|
||||
use crate::cpu;
|
||||
use crate::cpu::cpu_stark::CpuStark;
|
||||
use crate::cross_table_lookup::CrossTableLookup;
|
||||
use crate::keccak::keccak_stark::KeccakStark;
|
||||
@ -68,6 +68,8 @@ mod tests {
|
||||
use crate::recursive_verifier::{
|
||||
add_virtual_all_proof, set_all_proof_target, verify_proof_circuit,
|
||||
};
|
||||
use crate::stark::Stark;
|
||||
use crate::util::trace_rows_to_poly_values;
|
||||
use crate::verifier::verify_proof;
|
||||
|
||||
const D: usize = 2;
|
||||
@ -78,39 +80,34 @@ mod tests {
|
||||
let cpu_stark = CpuStark::<F, D> {
|
||||
f: Default::default(),
|
||||
};
|
||||
let cpu_rows = 1 << 4;
|
||||
let cpu_rows = 256;
|
||||
|
||||
let keccak_stark = KeccakStark::<F, D> {
|
||||
f: Default::default(),
|
||||
};
|
||||
let keccak_rows = 1 << 3;
|
||||
let keccak_rows = 256;
|
||||
let keccak_looked_col = 3;
|
||||
|
||||
let mut cpu_trace = vec![PolynomialValues::zero(cpu_rows); 10];
|
||||
let mut keccak_trace = vec![PolynomialValues::zero(keccak_rows); 7];
|
||||
let mut cpu_trace_rows = vec![];
|
||||
for i in 0..cpu_rows {
|
||||
let mut cpu_trace_row = [F::ZERO; CpuStark::<F, D>::COLUMNS];
|
||||
cpu_trace_row[cpu::columns::IS_CPU_CYCLE] = F::ONE;
|
||||
cpu_trace_row[cpu::columns::OPCODE] = F::from_canonical_usize(i);
|
||||
cpu_stark.generate(&mut cpu_trace_row);
|
||||
cpu_trace_rows.push(cpu_trace_row);
|
||||
}
|
||||
let cpu_trace = trace_rows_to_poly_values(cpu_trace_rows);
|
||||
|
||||
let vs0 = (0..keccak_rows)
|
||||
.map(F::from_canonical_usize)
|
||||
.collect::<Vec<_>>();
|
||||
let vs1 = (1..=keccak_rows)
|
||||
.map(F::from_canonical_usize)
|
||||
.collect::<Vec<_>>();
|
||||
let start = thread_rng().gen_range(0..cpu_rows - keccak_rows);
|
||||
|
||||
let default = vec![F::ONE; 2];
|
||||
|
||||
cpu_trace[2].values = vec![default[0]; cpu_rows];
|
||||
cpu_trace[2].values[start..start + keccak_rows].copy_from_slice(&vs0);
|
||||
cpu_trace[4].values = vec![default[1]; cpu_rows];
|
||||
cpu_trace[4].values[start..start + keccak_rows].copy_from_slice(&vs1);
|
||||
|
||||
keccak_trace[3].values[..].copy_from_slice(&vs0);
|
||||
keccak_trace[5].values[..].copy_from_slice(&vs1);
|
||||
let mut keccak_trace =
|
||||
vec![PolynomialValues::zero(keccak_rows); KeccakStark::<F, D>::COLUMNS];
|
||||
keccak_trace[keccak_looked_col] = cpu_trace[cpu::columns::OPCODE].clone();
|
||||
|
||||
let default = vec![F::ZERO; 2];
|
||||
let cross_table_lookups = vec![CrossTableLookup {
|
||||
looking_tables: vec![Table::Cpu],
|
||||
looking_columns: vec![vec![2, 4]],
|
||||
looking_columns: vec![vec![cpu::columns::OPCODE]],
|
||||
looked_table: Table::Keccak,
|
||||
looked_columns: vec![3, 5],
|
||||
looked_columns: vec![keccak_looked_col],
|
||||
default,
|
||||
}];
|
||||
|
||||
|
||||
135
evm/src/cpu/columns.rs
Normal file
135
evm/src/cpu/columns.rs
Normal file
@ -0,0 +1,135 @@
|
||||
// Filter. 1 if the row corresponds to a cycle of execution and 0 otherwise.
|
||||
// Lets us re-use decode columns in non-cycle rows.
|
||||
pub const IS_CPU_CYCLE: usize = 0;
|
||||
|
||||
// If CPU cycle: The opcode being decoded, in {0, ..., 255}.
|
||||
pub const OPCODE: usize = IS_CPU_CYCLE + 1;
|
||||
|
||||
// If CPU cycle: flags for EVM instructions. PUSHn, DUPn, and SWAPn only get one flag each. Invalid
|
||||
// opcodes are split between a number of flags for practical reasons. Exactly one of these flags
|
||||
// must be 1.
|
||||
pub const IS_STOP: usize = OPCODE + 1;
|
||||
pub const IS_ADD: usize = IS_STOP + 1;
|
||||
pub const IS_MUL: usize = IS_ADD + 1;
|
||||
pub const IS_SUB: usize = IS_MUL + 1;
|
||||
pub const IS_DIV: usize = IS_SUB + 1;
|
||||
pub const IS_SDIV: usize = IS_DIV + 1;
|
||||
pub const IS_MOD: usize = IS_SDIV + 1;
|
||||
pub const IS_SMOD: usize = IS_MOD + 1;
|
||||
pub const IS_ADDMOD: usize = IS_SMOD + 1;
|
||||
pub const IS_MULMOD: usize = IS_ADDMOD + 1;
|
||||
pub const IS_EXP: usize = IS_MULMOD + 1;
|
||||
pub const IS_SIGNEXTEND: usize = IS_EXP + 1;
|
||||
pub const IS_LT: usize = IS_SIGNEXTEND + 1;
|
||||
pub const IS_GT: usize = IS_LT + 1;
|
||||
pub const IS_SLT: usize = IS_GT + 1;
|
||||
pub const IS_SGT: usize = IS_SLT + 1;
|
||||
pub const IS_EQ: usize = IS_SGT + 1;
|
||||
pub const IS_ISZERO: usize = IS_EQ + 1;
|
||||
pub const IS_AND: usize = IS_ISZERO + 1;
|
||||
pub const IS_OR: usize = IS_AND + 1;
|
||||
pub const IS_XOR: usize = IS_OR + 1;
|
||||
pub const IS_NOT: usize = IS_XOR + 1;
|
||||
pub const IS_BYTE: usize = IS_NOT + 1;
|
||||
pub const IS_SHL: usize = IS_BYTE + 1;
|
||||
pub const IS_SHR: usize = IS_SHL + 1;
|
||||
pub const IS_SAR: usize = IS_SHR + 1;
|
||||
pub const IS_SHA3: usize = IS_SAR + 1;
|
||||
pub const IS_ADDRESS: usize = IS_SHA3 + 1;
|
||||
pub const IS_BALANCE: usize = IS_ADDRESS + 1;
|
||||
pub const IS_ORIGIN: usize = IS_BALANCE + 1;
|
||||
pub const IS_CALLER: usize = IS_ORIGIN + 1;
|
||||
pub const IS_CALLVALUE: usize = IS_CALLER + 1;
|
||||
pub const IS_CALLDATALOAD: usize = IS_CALLVALUE + 1;
|
||||
pub const IS_CALLDATASIZE: usize = IS_CALLDATALOAD + 1;
|
||||
pub const IS_CALLDATACOPY: usize = IS_CALLDATASIZE + 1;
|
||||
pub const IS_CODESIZE: usize = IS_CALLDATACOPY + 1;
|
||||
pub const IS_CODECOPY: usize = IS_CODESIZE + 1;
|
||||
pub const IS_GASPRICE: usize = IS_CODECOPY + 1;
|
||||
pub const IS_EXTCODESIZE: usize = IS_GASPRICE + 1;
|
||||
pub const IS_EXTCODECOPY: usize = IS_EXTCODESIZE + 1;
|
||||
pub const IS_RETURNDATASIZE: usize = IS_EXTCODECOPY + 1;
|
||||
pub const IS_RETURNDATACOPY: usize = IS_RETURNDATASIZE + 1;
|
||||
pub const IS_EXTCODEHASH: usize = IS_RETURNDATACOPY + 1;
|
||||
pub const IS_BLOCKHASH: usize = IS_EXTCODEHASH + 1;
|
||||
pub const IS_COINBASE: usize = IS_BLOCKHASH + 1;
|
||||
pub const IS_TIMESTAMP: usize = IS_COINBASE + 1;
|
||||
pub const IS_NUMBER: usize = IS_TIMESTAMP + 1;
|
||||
pub const IS_DIFFICULTY: usize = IS_NUMBER + 1;
|
||||
pub const IS_GASLIMIT: usize = IS_DIFFICULTY + 1;
|
||||
pub const IS_CHAINID: usize = IS_GASLIMIT + 1;
|
||||
pub const IS_SELFBALANCE: usize = IS_CHAINID + 1;
|
||||
pub const IS_BASEFEE: usize = IS_SELFBALANCE + 1;
|
||||
pub const IS_POP: usize = IS_BASEFEE + 1;
|
||||
pub const IS_MLOAD: usize = IS_POP + 1;
|
||||
pub const IS_MSTORE: usize = IS_MLOAD + 1;
|
||||
pub const IS_MSTORE8: usize = IS_MSTORE + 1;
|
||||
pub const IS_SLOAD: usize = IS_MSTORE8 + 1;
|
||||
pub const IS_SSTORE: usize = IS_SLOAD + 1;
|
||||
pub const IS_JUMP: usize = IS_SSTORE + 1;
|
||||
pub const IS_JUMPI: usize = IS_JUMP + 1;
|
||||
pub const IS_PC: usize = IS_JUMPI + 1;
|
||||
pub const IS_MSIZE: usize = IS_PC + 1;
|
||||
pub const IS_GAS: usize = IS_MSIZE + 1;
|
||||
pub const IS_JUMPDEST: usize = IS_GAS + 1;
|
||||
// Find the number of bytes to push by reading the bottom 5 bits of the opcode.
|
||||
pub const IS_PUSH: usize = IS_JUMPDEST + 1;
|
||||
// Find the stack offset to duplicate by reading the bottom 4 bits of the opcode.
|
||||
pub const IS_DUP: usize = IS_PUSH + 1;
|
||||
// Find the stack offset to swap with by reading the bottom 4 bits of the opcode.
|
||||
pub const IS_SWAP: usize = IS_DUP + 1;
|
||||
pub const IS_LOG0: usize = IS_SWAP + 1;
|
||||
pub const IS_LOG1: usize = IS_LOG0 + 1;
|
||||
pub const IS_LOG2: usize = IS_LOG1 + 1;
|
||||
pub const IS_LOG3: usize = IS_LOG2 + 1;
|
||||
pub const IS_LOG4: usize = IS_LOG3 + 1;
|
||||
pub const IS_CREATE: usize = IS_LOG4 + 1;
|
||||
pub const IS_CALL: usize = IS_CREATE + 1;
|
||||
pub const IS_CALLCODE: usize = IS_CALL + 1;
|
||||
pub const IS_RETURN: usize = IS_CALLCODE + 1;
|
||||
pub const IS_DELEGATECALL: usize = IS_RETURN + 1;
|
||||
pub const IS_CREATE2: usize = IS_DELEGATECALL + 1;
|
||||
pub const IS_STATICCALL: usize = IS_CREATE2 + 1;
|
||||
pub const IS_REVERT: usize = IS_STATICCALL + 1;
|
||||
pub const IS_SELFDESTRUCT: usize = IS_REVERT + 1;
|
||||
|
||||
pub const IS_INVALID_0: usize = IS_SELFDESTRUCT + 1;
|
||||
pub const IS_INVALID_1: usize = IS_INVALID_0 + 1;
|
||||
pub const IS_INVALID_2: usize = IS_INVALID_1 + 1;
|
||||
pub const IS_INVALID_3: usize = IS_INVALID_2 + 1;
|
||||
pub const IS_INVALID_4: usize = IS_INVALID_3 + 1;
|
||||
pub const IS_INVALID_5: usize = IS_INVALID_4 + 1;
|
||||
pub const IS_INVALID_6: usize = IS_INVALID_5 + 1;
|
||||
pub const IS_INVALID_7: usize = IS_INVALID_6 + 1;
|
||||
pub const IS_INVALID_8: usize = IS_INVALID_7 + 1;
|
||||
pub const IS_INVALID_9: usize = IS_INVALID_8 + 1;
|
||||
pub const IS_INVALID_10: usize = IS_INVALID_9 + 1;
|
||||
pub const IS_INVALID_11: usize = IS_INVALID_10 + 1;
|
||||
pub const IS_INVALID_12: usize = IS_INVALID_11 + 1;
|
||||
pub const IS_INVALID_13: usize = IS_INVALID_12 + 1;
|
||||
pub const IS_INVALID_14: usize = IS_INVALID_13 + 1;
|
||||
pub const IS_INVALID_15: usize = IS_INVALID_14 + 1;
|
||||
pub const IS_INVALID_16: usize = IS_INVALID_15 + 1;
|
||||
pub const IS_INVALID_17: usize = IS_INVALID_16 + 1;
|
||||
pub const IS_INVALID_18: usize = IS_INVALID_17 + 1;
|
||||
pub const IS_INVALID_19: usize = IS_INVALID_18 + 1;
|
||||
pub const IS_INVALID_20: usize = IS_INVALID_19 + 1;
|
||||
// An instruction is invalid if _any_ of the above flags is 1.
|
||||
|
||||
pub const START_INSTRUCTION_FLAGS: usize = IS_STOP;
|
||||
pub const END_INSTRUCTION_FLAGS: usize = IS_INVALID_20 + 1;
|
||||
|
||||
// If CPU cycle: the opcode, broken up into bits.
|
||||
// **big-endian** order
|
||||
pub const OPCODE_BITS: [usize; 8] = [
|
||||
END_INSTRUCTION_FLAGS,
|
||||
END_INSTRUCTION_FLAGS + 1,
|
||||
END_INSTRUCTION_FLAGS + 2,
|
||||
END_INSTRUCTION_FLAGS + 3,
|
||||
END_INSTRUCTION_FLAGS + 4,
|
||||
END_INSTRUCTION_FLAGS + 5,
|
||||
END_INSTRUCTION_FLAGS + 6,
|
||||
END_INSTRUCTION_FLAGS + 7,
|
||||
];
|
||||
|
||||
pub const NUM_CPU_COLUMNS: usize = OPCODE_BITS[OPCODE_BITS.len() - 1] + 1;
|
||||
@ -5,6 +5,8 @@ use plonky2::field::packed_field::PackedField;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
|
||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||
use crate::cpu::columns;
|
||||
use crate::cpu::decode;
|
||||
use crate::permutation::PermutationPair;
|
||||
use crate::stark::Stark;
|
||||
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
|
||||
@ -14,26 +16,34 @@ pub struct CpuStark<F, const D: usize> {
|
||||
pub f: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: RichField, const D: usize> CpuStark<F, D> {
|
||||
pub fn generate(&self, local_values: &mut [F; columns::NUM_CPU_COLUMNS]) {
|
||||
decode::generate(local_values);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D> {
|
||||
const COLUMNS: usize = 10;
|
||||
const COLUMNS: usize = columns::NUM_CPU_COLUMNS;
|
||||
const PUBLIC_INPUTS: usize = 0;
|
||||
|
||||
fn eval_packed_generic<FE, P, const D2: usize>(
|
||||
&self,
|
||||
_vars: StarkEvaluationVars<FE, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
|
||||
_yield_constr: &mut ConstraintConsumer<P>,
|
||||
vars: StarkEvaluationVars<FE, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
|
||||
yield_constr: &mut ConstraintConsumer<P>,
|
||||
) where
|
||||
FE: FieldExtension<D2, BaseField = F>,
|
||||
P: PackedField<Scalar = FE>,
|
||||
{
|
||||
decode::eval_packed_generic(vars.local_values, yield_constr);
|
||||
}
|
||||
|
||||
fn eval_ext_circuit(
|
||||
&self,
|
||||
_builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||
_vars: StarkEvaluationTargets<D, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
|
||||
_yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||
vars: StarkEvaluationTargets<D, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
|
||||
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||
) {
|
||||
decode::eval_ext_circuit(builder, vars.local_values, yield_constr);
|
||||
}
|
||||
|
||||
fn constraint_degree(&self) -> usize {
|
||||
@ -41,7 +51,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
}
|
||||
|
||||
fn permutation_pairs(&self) -> Vec<PermutationPair> {
|
||||
vec![PermutationPair::singletons(8, 9)]
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +64,6 @@ mod tests {
|
||||
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: remove this when constraints are no longer all 0.
|
||||
fn test_stark_degree() -> Result<()> {
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
|
||||
264
evm/src/cpu/decode.rs
Normal file
264
evm/src/cpu/decode.rs
Normal file
@ -0,0 +1,264 @@
|
||||
use plonky2::field::extension_field::Extendable;
|
||||
use plonky2::field::field_types::Field;
|
||||
use plonky2::field::packed_field::PackedField;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::iop::ext_target::ExtensionTarget;
|
||||
|
||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||
use crate::cpu::columns;
|
||||
|
||||
// List of opcode blocks
|
||||
// Each block corresponds to exactly one flag, and each flag corresponds to exactly one block.
|
||||
// Each block of opcodes:
|
||||
// - is contiguous
|
||||
// - has a length that is a power of 2
|
||||
// - its start index is a multiple of its length (it is aligned)
|
||||
// These properties permit us to check if an opcode belongs to a block of length 2^n by checking its
|
||||
// top 8-n bits.
|
||||
const OPCODES: [(u64, usize, usize); 102] = [
|
||||
// (start index of block, number of top bits to check (log2), flag column)
|
||||
(0x00, 0, columns::IS_STOP),
|
||||
(0x01, 0, columns::IS_ADD),
|
||||
(0x02, 0, columns::IS_MUL),
|
||||
(0x03, 0, columns::IS_SUB),
|
||||
(0x04, 0, columns::IS_DIV),
|
||||
(0x05, 0, columns::IS_SDIV),
|
||||
(0x06, 0, columns::IS_MOD),
|
||||
(0x07, 0, columns::IS_SMOD),
|
||||
(0x08, 0, columns::IS_ADDMOD),
|
||||
(0x09, 0, columns::IS_MULMOD),
|
||||
(0x0a, 0, columns::IS_EXP),
|
||||
(0x0b, 0, columns::IS_SIGNEXTEND),
|
||||
(0x0c, 2, columns::IS_INVALID_0), // 0x0c-0x0f
|
||||
(0x10, 0, columns::IS_LT),
|
||||
(0x11, 0, columns::IS_GT),
|
||||
(0x12, 0, columns::IS_SLT),
|
||||
(0x13, 0, columns::IS_SGT),
|
||||
(0x14, 0, columns::IS_EQ),
|
||||
(0x15, 0, columns::IS_ISZERO),
|
||||
(0x16, 0, columns::IS_AND),
|
||||
(0x17, 0, columns::IS_OR),
|
||||
(0x18, 0, columns::IS_XOR),
|
||||
(0x19, 0, columns::IS_NOT),
|
||||
(0x1a, 0, columns::IS_BYTE),
|
||||
(0x1b, 0, columns::IS_SHL),
|
||||
(0x1c, 0, columns::IS_SHR),
|
||||
(0x1d, 0, columns::IS_SAR),
|
||||
(0x1e, 1, columns::IS_INVALID_1), // 0x1e-0x1f
|
||||
(0x20, 0, columns::IS_SHA3),
|
||||
(0x21, 0, columns::IS_INVALID_2),
|
||||
(0x22, 1, columns::IS_INVALID_3), // 0x22-0x23
|
||||
(0x24, 2, columns::IS_INVALID_4), // 0x24-0x27
|
||||
(0x28, 3, columns::IS_INVALID_5), // 0x28-0x2f
|
||||
(0x30, 0, columns::IS_ADDRESS),
|
||||
(0x31, 0, columns::IS_BALANCE),
|
||||
(0x32, 0, columns::IS_ORIGIN),
|
||||
(0x33, 0, columns::IS_CALLER),
|
||||
(0x34, 0, columns::IS_CALLVALUE),
|
||||
(0x35, 0, columns::IS_CALLDATALOAD),
|
||||
(0x36, 0, columns::IS_CALLDATASIZE),
|
||||
(0x37, 0, columns::IS_CALLDATACOPY),
|
||||
(0x38, 0, columns::IS_CODESIZE),
|
||||
(0x39, 0, columns::IS_CODECOPY),
|
||||
(0x3a, 0, columns::IS_GASPRICE),
|
||||
(0x3b, 0, columns::IS_EXTCODESIZE),
|
||||
(0x3c, 0, columns::IS_EXTCODECOPY),
|
||||
(0x3d, 0, columns::IS_RETURNDATASIZE),
|
||||
(0x3e, 0, columns::IS_RETURNDATACOPY),
|
||||
(0x3f, 0, columns::IS_EXTCODEHASH),
|
||||
(0x40, 0, columns::IS_BLOCKHASH),
|
||||
(0x41, 0, columns::IS_COINBASE),
|
||||
(0x42, 0, columns::IS_TIMESTAMP),
|
||||
(0x43, 0, columns::IS_NUMBER),
|
||||
(0x44, 0, columns::IS_DIFFICULTY),
|
||||
(0x45, 0, columns::IS_GASLIMIT),
|
||||
(0x46, 0, columns::IS_CHAINID),
|
||||
(0x47, 0, columns::IS_SELFBALANCE),
|
||||
(0x48, 0, columns::IS_BASEFEE),
|
||||
(0x49, 0, columns::IS_INVALID_6),
|
||||
(0x4a, 1, columns::IS_INVALID_7), // 0x4a-0x4b
|
||||
(0x4c, 2, columns::IS_INVALID_8), // 0x4c-0x4f
|
||||
(0x50, 0, columns::IS_POP),
|
||||
(0x51, 0, columns::IS_MLOAD),
|
||||
(0x52, 0, columns::IS_MSTORE),
|
||||
(0x53, 0, columns::IS_MSTORE8),
|
||||
(0x54, 0, columns::IS_SLOAD),
|
||||
(0x55, 0, columns::IS_SSTORE),
|
||||
(0x56, 0, columns::IS_JUMP),
|
||||
(0x57, 0, columns::IS_JUMPI),
|
||||
(0x58, 0, columns::IS_PC),
|
||||
(0x59, 0, columns::IS_MSIZE),
|
||||
(0x5a, 0, columns::IS_GAS),
|
||||
(0x5b, 0, columns::IS_JUMPDEST),
|
||||
(0x5c, 2, columns::IS_INVALID_9), // 0x5c-0x5f
|
||||
(0x60, 5, columns::IS_PUSH), // 0x60-0x7f
|
||||
(0x80, 4, columns::IS_DUP), // 0x80-0x8f
|
||||
(0x90, 4, columns::IS_SWAP), // 0x90-0x9f
|
||||
(0xa0, 0, columns::IS_LOG0),
|
||||
(0xa1, 0, columns::IS_LOG1),
|
||||
(0xa2, 0, columns::IS_LOG2),
|
||||
(0xa3, 0, columns::IS_LOG3),
|
||||
(0xa4, 0, columns::IS_LOG4),
|
||||
(0xa5, 0, columns::IS_INVALID_10),
|
||||
(0xa6, 1, columns::IS_INVALID_11), // 0xa6-0xa7
|
||||
(0xa8, 3, columns::IS_INVALID_12), // 0xa8-0xaf
|
||||
(0xb0, 4, columns::IS_INVALID_13), // 0xb0-0xbf
|
||||
(0xc0, 5, columns::IS_INVALID_14), // 0xc0-0xdf
|
||||
(0xe0, 4, columns::IS_INVALID_15), // 0xe0-0xef
|
||||
(0xf0, 0, columns::IS_CREATE),
|
||||
(0xf1, 0, columns::IS_CALL),
|
||||
(0xf2, 0, columns::IS_CALLCODE),
|
||||
(0xf3, 0, columns::IS_RETURN),
|
||||
(0xf4, 0, columns::IS_DELEGATECALL),
|
||||
(0xf5, 0, columns::IS_CREATE2),
|
||||
(0xf6, 1, columns::IS_INVALID_16), // 0xf6-0xf7
|
||||
(0xf8, 1, columns::IS_INVALID_17), // 0xf8-0xf9
|
||||
(0xfa, 0, columns::IS_STATICCALL),
|
||||
(0xfb, 0, columns::IS_INVALID_18),
|
||||
(0xfc, 0, columns::IS_INVALID_19),
|
||||
(0xfd, 0, columns::IS_REVERT),
|
||||
(0xfe, 0, columns::IS_INVALID_20),
|
||||
(0xff, 0, columns::IS_SELFDESTRUCT),
|
||||
];
|
||||
|
||||
pub fn generate<F: RichField>(lv: &mut [F; columns::NUM_CPU_COLUMNS]) {
|
||||
let cycle_filter = lv[columns::IS_CPU_CYCLE];
|
||||
if cycle_filter == F::ZERO {
|
||||
return;
|
||||
}
|
||||
// This assert is not _strictly_ necessary, but I include it as a sanity check.
|
||||
assert_eq!(cycle_filter, F::ONE, "cycle_filter should be 0 or 1");
|
||||
|
||||
let opcode = lv[columns::OPCODE].to_canonical_u64();
|
||||
assert!(opcode < 256, "opcode should be in {{0, ..., 255}}");
|
||||
|
||||
for (i, &col) in columns::OPCODE_BITS.iter().enumerate() {
|
||||
let bit = (opcode >> (7 - i)) & 1;
|
||||
lv[col] = F::from_canonical_u64(bit);
|
||||
}
|
||||
|
||||
let top_bits: [u64; 9] = [
|
||||
0,
|
||||
opcode & 0x80,
|
||||
opcode & 0xc0,
|
||||
opcode & 0xe0,
|
||||
opcode & 0xf0,
|
||||
opcode & 0xf8,
|
||||
opcode & 0xfc,
|
||||
opcode & 0xfe,
|
||||
opcode,
|
||||
];
|
||||
|
||||
for (oc, block_length, col) in OPCODES {
|
||||
lv[col] = F::from_bool(top_bits[8 - block_length] == oc);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_packed_generic<P: PackedField>(
|
||||
lv: &[P; columns::NUM_CPU_COLUMNS],
|
||||
yield_constr: &mut ConstraintConsumer<P>,
|
||||
) {
|
||||
let cycle_filter = lv[columns::IS_CPU_CYCLE];
|
||||
|
||||
// Ensure that the opcode bits are valid: each has to be either 0 or 1, and they must match
|
||||
// the opcode. Note that this also validates that this implicitly range-checks the opcode.
|
||||
let bits = columns::OPCODE_BITS.map(|i| lv[i]);
|
||||
// First check that the bits are either 0 or 1.
|
||||
for bit in bits {
|
||||
yield_constr.constraint(cycle_filter * bit * (bit - P::ONES));
|
||||
}
|
||||
|
||||
// top_bits[i] is the opcode with all but the top i bits cleared.
|
||||
let top_bits = {
|
||||
let mut top_bits = [P::ZEROS; 9];
|
||||
for i in 0..8 {
|
||||
top_bits[i + 1] = top_bits[i] + bits[i] * P::Scalar::from_canonical_u64(1 << (7 - i));
|
||||
}
|
||||
top_bits
|
||||
};
|
||||
|
||||
// Now check that they match the opcode.
|
||||
let opcode = lv[columns::OPCODE];
|
||||
yield_constr.constraint(cycle_filter * (opcode - top_bits[8]));
|
||||
|
||||
// Check that the instruction flags are valid.
|
||||
// First, check that they are all either 0 or 1.
|
||||
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
|
||||
yield_constr.constraint(cycle_filter * flag * (flag - P::ONES));
|
||||
}
|
||||
// Now check that exactly one is 1.
|
||||
let flag_sum: P = (columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS)
|
||||
.into_iter()
|
||||
.map(|i| lv[i])
|
||||
.sum();
|
||||
yield_constr.constraint(cycle_filter * (P::ONES - flag_sum));
|
||||
|
||||
// Finally, classify all opcodes into blocks
|
||||
for (oc, block_length, col) in OPCODES {
|
||||
let constr = lv[col] * (top_bits[8 - block_length] - P::Scalar::from_canonical_u64(oc));
|
||||
yield_constr.constraint(cycle_filter * constr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||
lv: &[ExtensionTarget<D>; columns::NUM_CPU_COLUMNS],
|
||||
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||
) {
|
||||
let cycle_filter = lv[columns::IS_CPU_CYCLE];
|
||||
|
||||
// Ensure that the opcode bits are valid: each has to be either 0 or 1, and they must match
|
||||
// the opcode. Note that this also validates that this implicitly range-checks the opcode.
|
||||
let bits = columns::OPCODE_BITS.map(|i| lv[i]);
|
||||
// First check that the bits are either 0 or 1.
|
||||
for bit in bits {
|
||||
let constr = builder.mul_sub_extension(bit, bit, bit);
|
||||
let constr = builder.mul_extension(cycle_filter, constr);
|
||||
yield_constr.constraint(builder, constr);
|
||||
}
|
||||
|
||||
let top_bits = {
|
||||
let mut top_bits = [builder.zero_extension(); 9];
|
||||
for i in 0..8 {
|
||||
top_bits[i + 1] = builder.mul_const_add_extension(
|
||||
F::from_canonical_u64(1 << (7 - i)),
|
||||
bits[i],
|
||||
top_bits[i],
|
||||
);
|
||||
}
|
||||
top_bits
|
||||
};
|
||||
|
||||
// Now check that the bits match the opcode.
|
||||
{
|
||||
let constr = builder.sub_extension(lv[columns::OPCODE], top_bits[8]);
|
||||
let constr = builder.mul_extension(cycle_filter, constr);
|
||||
yield_constr.constraint(builder, constr);
|
||||
};
|
||||
|
||||
// Check that the instruction flags are valid.
|
||||
// First, check that they are all either 0 or 1.
|
||||
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
|
||||
let constr = builder.mul_sub_extension(flag, flag, flag);
|
||||
let constr = builder.mul_extension(cycle_filter, constr);
|
||||
yield_constr.constraint(builder, constr);
|
||||
}
|
||||
// Now check that they sum to 1.
|
||||
{
|
||||
let mut constr = builder.one_extension();
|
||||
for &flag in &lv[columns::START_INSTRUCTION_FLAGS..columns::END_INSTRUCTION_FLAGS] {
|
||||
constr = builder.sub_extension(constr, flag);
|
||||
}
|
||||
constr = builder.mul_extension(cycle_filter, constr);
|
||||
yield_constr.constraint(builder, constr);
|
||||
}
|
||||
|
||||
for (oc, block_length, col) in OPCODES {
|
||||
let flag = lv[col];
|
||||
let constr = builder.constant_extension(F::from_canonical_u64(oc).into());
|
||||
let constr = builder.sub_extension(top_bits[8 - block_length], constr);
|
||||
let constr = builder.mul_extension(flag, constr);
|
||||
let constr = builder.mul_extension(cycle_filter, constr);
|
||||
yield_constr.constraint(builder, constr);
|
||||
}
|
||||
}
|
||||
@ -1 +1,3 @@
|
||||
pub(crate) mod columns;
|
||||
pub mod cpu_stark;
|
||||
pub(crate) mod decode;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user