diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 2285a170..d65fb30c 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -13,3 +13,4 @@ itertools = "0.10.0" log = "0.4.14" rayon = "1.5.1" rand = "0.8.5" +rand_chacha = "0.3.1" diff --git a/evm/src/keccak/logic.rs b/evm/src/keccak/logic.rs new file mode 100644 index 00000000..7d248258 --- /dev/null +++ b/evm/src/keccak/logic.rs @@ -0,0 +1,65 @@ +use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::PrimeField64; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +pub(crate) fn xor(xs: [F; N]) -> F { + xs.into_iter().fold(F::ZERO, |acc, x| { + debug_assert!(x.is_zero() || x.is_one()); + F::from_canonical_u64(acc.to_canonical_u64() ^ x.to_canonical_u64()) + }) +} + +/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. +pub(crate) fn xor_gen(x: P, y: P) -> P { + x + y - x * y.doubles() +} + +/// Computes the arithmetic generalization of `xor3(x, y, z)`. +pub(crate) fn xor3_gen(x: P, y: P, z: P) -> P { + xor_gen(x, xor_gen(y, z)) +} + +/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. +pub(crate) fn xor_gen_circuit, const D: usize>( + builder: &mut CircuitBuilder, + x: ExtensionTarget, + y: ExtensionTarget, +) -> ExtensionTarget { + let sum = builder.add_extension(x, y); + builder.arithmetic_extension(-F::TWO, F::ONE, x, y, sum) +} + +/// Computes the arithmetic generalization of `xor(x, y)`, i.e. `x + y - 2 x y`. +pub(crate) fn xor3_gen_circuit, const D: usize>( + builder: &mut CircuitBuilder, + x: ExtensionTarget, + y: ExtensionTarget, + z: ExtensionTarget, +) -> ExtensionTarget { + let x_xor_y = xor_gen_circuit(builder, x, y); + xor_gen_circuit(builder, x_xor_y, z) +} + +pub(crate) fn andn(x: F, y: F) -> F { + debug_assert!(x.is_zero() || x.is_one()); + debug_assert!(y.is_zero() || y.is_one()); + let x = x.to_canonical_u64(); + let y = y.to_canonical_u64(); + F::from_canonical_u64(!x & y) +} + +pub(crate) fn andn_gen(x: P, y: P) -> P { + (P::ONES - x) * y +} + +pub(crate) fn andn_gen_circuit, const D: usize>( + builder: &mut CircuitBuilder, + x: ExtensionTarget, + y: ExtensionTarget, +) -> ExtensionTarget { + // (1 - x) y = -xy + y + builder.arithmetic_extension(F::NEG_ONE, F::ONE, x, y, y) +} diff --git a/evm/src/keccak/registers.rs b/evm/src/keccak/registers.rs new file mode 100644 index 00000000..f3e847b1 --- /dev/null +++ b/evm/src/keccak/registers.rs @@ -0,0 +1,252 @@ +use crate::keccak::keccak_stark::NUM_ROUNDS; + +/// A register which is set to 1 if we are in the `i`th round, otherwise 0. +pub(crate) const fn reg_step(i: usize) -> usize { + debug_assert!(i < NUM_ROUNDS); + i +} + +const R: [[u8; 5]; 5] = [ + [0, 18, 41, 3, 36], + [1, 2, 45, 10, 44], + [62, 61, 15, 43, 6], + [28, 56, 21, 25, 55], + [27, 14, 8, 39, 20], +]; + +const RC: [u64; 24] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +const RC_BITS: [[u8; 64]; 24] = [ + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], +]; + +pub(crate) const fn rc_value_bit(round: usize, bit_index: usize) -> u8 { + RC_BITS[round][bit_index] +} + +pub(crate) const fn rc_value(round: usize) -> u64 { + RC[round] +} + +const START_A: usize = NUM_ROUNDS; +pub(crate) const fn reg_a(x: usize, y: usize, z: usize) -> usize { + debug_assert!(x < 5); + debug_assert!(y < 5); + debug_assert!(z < 64); + START_A + x * 64 * 5 + y * 64 + z +} + +// C_partial[x] = xor(A[x, 0], A[x, 1], A[x, 2]) +const START_C_PARTIAL: usize = START_A + 5 * 5 * 64; +pub(crate) const fn reg_c_partial(x: usize, z: usize) -> usize { + START_C_PARTIAL + x * 64 + z +} + +// C[x] = xor(C_partial[x], A[x, 3], A[x, 4]) +const START_C: usize = START_C_PARTIAL + 5 * 64; +pub(crate) const fn reg_c(x: usize, z: usize) -> usize { + START_C + x * 64 + z +} + +// D is inlined. +// const fn reg_d(x: usize, z: usize) {} + +// A'[x, y] = xor(A[x, y], D[x]) +// = xor(A[x, y], C[x - 1], ROT(C[x + 1], 1)) +const START_A_PRIME: usize = START_C + 5 * 64; +pub(crate) const fn reg_a_prime(x: usize, y: usize, z: usize) -> usize { + debug_assert!(x < 5); + debug_assert!(y < 5); + debug_assert!(z < 64); + START_A_PRIME + x * 64 * 5 + y * 64 + z +} + +pub(crate) const fn reg_b(x: usize, y: usize, z: usize) -> usize { + debug_assert!(x < 5); + debug_assert!(y < 5); + debug_assert!(z < 64); + // B is just a rotation of A', so these are aliases for A' registers. + // From the spec, + // B[y, (2x + 3y) % 5] = ROT(A'[x, y], r[x, y]) + // So, + // B[x, y] = f((x + 3y) % 5, x) + // where f(a, b) = ROT(A'[a, b], r[a, b]) + let a = (x + 3 * y) % 5; + let b = x; + let rot = R[a][b] as usize; + reg_a_prime(a, b, (z + rot) % 64) +} + +// A''[x, y] = xor(B[x, y], andn(B[x + 1, y], B[x + 2, y])). +// A''[0, 0] is additionally xor'd with RC. +const START_A_PRIME_PRIME: usize = START_A_PRIME + 5 * 5 * 64; +pub(crate) const fn reg_a_prime_prime(x: usize, y: usize) -> usize { + debug_assert!(x < 5); + debug_assert!(y < 5); + START_A_PRIME_PRIME + x * 2 * 5 + y * 2 +} + +const START_A_PRIME_PRIME_0_0_BITS: usize = START_A_PRIME_PRIME + 5 * 5 * 2; +pub(crate) const fn reg_a_prime_prime_0_0_bit(i: usize) -> usize { + debug_assert!(i < 64); + START_A_PRIME_PRIME_0_0_BITS + i +} + +const REG_A_PRIME_PRIME_PRIME_0_0_LO: usize = START_A_PRIME_PRIME_0_0_BITS + 64; +const REG_A_PRIME_PRIME_PRIME_0_0_HI: usize = REG_A_PRIME_PRIME_PRIME_0_0_LO + 1; + +pub(crate) const fn reg_a_prime_prime_prime(x: usize, y: usize) -> usize { + debug_assert!(x < 5); + debug_assert!(y < 5); + if x == 0 && y == 0 { + REG_A_PRIME_PRIME_PRIME_0_0_LO + } else { + reg_a_prime_prime(x, y) + } +} + +pub(crate) const NUM_REGISTERS: usize = REG_A_PRIME_PRIME_PRIME_0_0_HI + 1; diff --git a/evm/src/keccak/round_flags.rs b/evm/src/keccak/round_flags.rs new file mode 100644 index 00000000..63128ba1 --- /dev/null +++ b/evm/src/keccak/round_flags.rs @@ -0,0 +1,40 @@ +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::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::keccak::keccak_stark::{NUM_PUBLIC_INPUTS, NUM_ROUNDS}; +use crate::keccak::registers::reg_step; +use crate::keccak::registers::NUM_REGISTERS; +use crate::vars::StarkEvaluationTargets; +use crate::vars::StarkEvaluationVars; + +pub(crate) fn eval_round_flags>( + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, +) { + // Initially, the first step flag should be 1 while the others should be 0. + yield_constr.constraint_first_row(vars.local_values[reg_step(0)] - F::ONE); + for i in 1..NUM_ROUNDS { + yield_constr.constraint_first_row(vars.local_values[reg_step(i)]); + } + + // TODO: Transition. +} + +pub(crate) fn eval_round_flags_recursively, const D: usize>( + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, +) { + let one = builder.one_extension(); + + // Initially, the first step flag should be 1 while the others should be 0. + let step_0_minus_1 = builder.sub_extension(vars.local_values[reg_step(0)], one); + yield_constr.constraint_first_row(builder, step_0_minus_1); + for i in 1..NUM_ROUNDS { + yield_constr.constraint_first_row(builder, vars.local_values[reg_step(i)]); + } +}