From cc1f677949b79531cfa174344c2e53c32bd7a2ae Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 2 Jun 2023 14:41:37 +0800 Subject: [PATCH] feat: upgrade `revm` to support lastest hardfork (#40) --- .github/workflows/ci.yaml | 2 +- rust-toolchain | 2 +- snark-verifier-sdk/src/evm.rs | 22 +- snark-verifier/Cargo.toml | 11 +- .../examples/evm-verifier-with-accumulator.rs | 21 +- snark-verifier/examples/evm-verifier.rs | 21 +- snark-verifier/src/loader/evm.rs | 10 +- snark-verifier/src/loader/evm/loader.rs | 48 +- snark-verifier/src/loader/evm/test.rs | 48 - snark-verifier/src/loader/evm/test/tui.rs | 943 ------------------ snark-verifier/src/loader/evm/util.rs | 13 +- .../src/loader/evm/util/executor.rs | 912 +---------------- snark-verifier/src/pcs/kzg/accumulator.rs | 2 - snark-verifier/src/pcs/kzg/decider.rs | 8 +- .../src/pcs/kzg/multiopen/bdfg21.rs | 4 +- snark-verifier/src/system/halo2.rs | 6 +- .../src/system/halo2/test/kzg/evm.rs | 16 +- .../src/system/halo2/transcript/evm.rs | 4 +- snark-verifier/src/util/arithmetic.rs | 4 +- snark-verifier/src/util/poly.rs | 2 +- 20 files changed, 107 insertions(+), 1992 deletions(-) delete mode 100644 snark-verifier/src/loader/evm/test.rs delete mode 100644 snark-verifier/src/loader/evm/test/tui.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 20a5ec2f..afcfca29 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: cache-on-failure: true - name: Install solc - run: (hash svm 2>/dev/null || cargo install --version 0.2.22 svm-rs) && svm install 0.8.17 && solc --version + run: (hash svm 2>/dev/null || cargo install --version 0.2.23 svm-rs) && svm install 0.8.20 && solc --version - name: Run test run: cargo test --all --all-features -- --nocapture diff --git a/rust-toolchain b/rust-toolchain index 75733293..cb908525 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.64.0 \ No newline at end of file +1.69.0 \ No newline at end of file diff --git a/snark-verifier-sdk/src/evm.rs b/snark-verifier-sdk/src/evm.rs index b072ce48..28eed0c8 100644 --- a/snark-verifier-sdk/src/evm.rs +++ b/snark-verifier-sdk/src/evm.rs @@ -3,7 +3,6 @@ use crate::{GWC, SHPLONK}; use super::{CircuitExt, PlonkVerifier}; #[cfg(feature = "display")] use ark_std::{end_timer, start_timer}; -use ethereum_types::Address; use halo2_proofs::{ halo2curves::bn256::{Bn256, Fq, Fr, G1Affine}, plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, @@ -23,7 +22,7 @@ use itertools::Itertools; use rand::{rngs::StdRng, SeedableRng}; pub use snark_verifier::loader::evm::encode_calldata; use snark_verifier::{ - loader::evm::{compile_yul, EvmLoader, ExecutorBuilder}, + loader::evm::{compile_yul, deploy_and_call, EvmLoader}, pcs::{ kzg::{KzgAccumulator, KzgAsVerifyingKey, KzgDecidingKey, KzgSuccinctVerifyingKey}, AccumulationDecider, AccumulationScheme, PolynomialCommitmentScheme, @@ -180,23 +179,8 @@ pub fn gen_evm_verifier_shplonk>( pub fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { let calldata = encode_calldata(&instances, &proof); - let success = { - let mut evm = ExecutorBuilder::default() - .with_gas_limit(u64::MAX.into()) - .build(); - - let caller = Address::from_low_u64_be(0xfe); - let verifier = evm - .deploy(caller, deployment_code.into(), 0.into()) - .address - .unwrap(); - let result = evm.call_raw(caller, verifier, calldata.into(), 0.into()); - - dbg!(result.gas_used); - - !result.reverted - }; - assert!(success); + let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); + dbg!(gas_cost); } pub fn write_calldata(instances: &[Vec], proof: &[u8], path: &Path) -> io::Result { diff --git a/snark-verifier/Cargo.toml b/snark-verifier/Cargo.toml index dfe572ad..735f3295 100644 --- a/snark-verifier/Cargo.toml +++ b/snark-verifier/Cargo.toml @@ -21,10 +21,7 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2", # loader_evm sha3 = { version = "0.10", optional = true } -bytes = { version = "1.1.0", default-features = false, optional = true } -primitive-types = { version = "0.12.1", default-features = false, features = ["std"], optional = true } -rlp = { version = "0.5.2", default-features = false, features = ["std"], optional = true } -revm = { version = "= 2.3.1", optional = true } +revm = { version = "3.3.0", optional = true } # loader_halo2 halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc", optional = true } @@ -40,17 +37,13 @@ paste = "1.0.7" # system_halo2 halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc" } -# loader_evm -crossterm = { version = "0.25" } -tui = { version = "0.19", default-features = false, features = ["crossterm"] } - [features] default = ["loader_evm", "loader_halo2", "system_halo2"] parallel = ["dep:rayon"] # loaders -loader_evm = ["dep:bytes", "dep:sha3", "dep:primitive-types", "dep:rlp", "dep:revm"] +loader_evm = ["dep:sha3", "dep:revm"] loader_halo2 = ["dep:halo2_proofs", "dep:halo2_wrong_ecc", "dep:poseidon"] # systems diff --git a/snark-verifier/examples/evm-verifier-with-accumulator.rs b/snark-verifier/examples/evm-verifier-with-accumulator.rs index 369600e3..f0ec0afa 100644 --- a/snark-verifier/examples/evm-verifier-with-accumulator.rs +++ b/snark-verifier/examples/evm-verifier-with-accumulator.rs @@ -17,7 +17,7 @@ use itertools::Itertools; use rand::rngs::OsRng; use snark_verifier::{ loader::{ - evm::{self, encode_calldata, Address, EvmLoader, ExecutorBuilder}, + evm::{self, deploy_and_call, encode_calldata, EvmLoader}, native::NativeLoader, }, pcs::kzg::{Gwc19, KzgAs, LimbsEncoding}, @@ -587,23 +587,8 @@ fn gen_aggregation_evm_verifier( fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { let calldata = encode_calldata(&instances, &proof); - let success = { - let mut evm = ExecutorBuilder::default() - .with_gas_limit(u64::MAX.into()) - .build(); - - let caller = Address::from_low_u64_be(0xfe); - let verifier = evm - .deploy(caller, deployment_code.into(), 0.into()) - .address - .unwrap(); - let result = evm.call_raw(caller, verifier, calldata.into(), 0.into()); - - dbg!(result.gas_used); - - !result.reverted - }; - assert!(success); + let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); + dbg!(gas_cost); } fn main() { diff --git a/snark-verifier/examples/evm-verifier.rs b/snark-verifier/examples/evm-verifier.rs index 6e58d330..3a0a412c 100644 --- a/snark-verifier/examples/evm-verifier.rs +++ b/snark-verifier/examples/evm-verifier.rs @@ -23,7 +23,7 @@ use halo2_proofs::{ use itertools::Itertools; use rand::{rngs::OsRng, RngCore}; use snark_verifier::{ - loader::evm::{self, encode_calldata, Address, EvmLoader, ExecutorBuilder}, + loader::evm::{self, deploy_and_call, encode_calldata, EvmLoader}, pcs::kzg::{Gwc19, KzgAs}, system::halo2::{compile, transcript::evm::EvmTranscript, Config}, verifier::{self, SnarkVerifier}, @@ -230,23 +230,8 @@ fn gen_evm_verifier( fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { let calldata = encode_calldata(&instances, &proof); - let success = { - let mut evm = ExecutorBuilder::default() - .with_gas_limit(u64::MAX.into()) - .build(); - - let caller = Address::from_low_u64_be(0xfe); - let verifier = evm - .deploy(caller, deployment_code.into(), 0.into()) - .address - .unwrap(); - let result = evm.call_raw(caller, verifier, calldata.into(), 0.into()); - - dbg!(result.gas_used); - - !result.reverted - }; - assert!(success); + let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); + dbg!(gas_cost); } fn main() { diff --git a/snark-verifier/src/loader/evm.rs b/snark-verifier/src/loader/evm.rs index e942b4a3..74ed0f4e 100644 --- a/snark-verifier/src/loader/evm.rs +++ b/snark-verifier/src/loader/evm.rs @@ -4,14 +4,8 @@ mod code; pub(crate) mod loader; pub(crate) mod util; -#[cfg(test)] -mod test; - pub use loader::{EcPoint, EvmLoader, Scalar}; pub use util::{ - compile_yul, encode_calldata, estimate_gas, fe_to_u256, modulus, u256_to_fe, Address, - ExecutorBuilder, H256, U256, U512, + compile_yul, deploy_and_call, encode_calldata, estimate_gas, fe_to_u256, modulus, u256_to_fe, + Address, B256, U256, U512, }; - -#[cfg(test)] -pub use test::execute; diff --git a/snark-verifier/src/loader/evm/loader.rs b/snark-verifier/src/loader/evm/loader.rs index 6a756ced..44d5dd27 100644 --- a/snark-verifier/src/loader/evm/loader.rs +++ b/snark-verifier/src/loader/evm/loader.rs @@ -56,14 +56,10 @@ pub struct EvmLoader { code: RefCell, ptr: RefCell, cache: RefCell>, - #[cfg(test)] - gas_metering_ids: RefCell>, } fn hex_encode_u256(value: &U256) -> String { - let mut bytes = [0; 32]; - value.to_big_endian(&mut bytes); - format!("0x{}", hex::encode(bytes)) + format!("0x{}", hex::encode(value.to_be_bytes::<32>())) } impl EvmLoader { @@ -83,8 +79,6 @@ impl EvmLoader { code: RefCell::new(code), ptr: Default::default(), cache: Default::default(), - #[cfg(test)] - gas_metering_ids: RefCell::new(Vec::new()), }) } @@ -326,11 +320,11 @@ impl EvmLoader { fn invert(self: &Rc, scalar: &Scalar) -> Scalar { let rd_ptr = self.allocate(0x20); let [cd_ptr, ..] = [ - &self.scalar(Value::Constant(0x20.into())), - &self.scalar(Value::Constant(0x20.into())), - &self.scalar(Value::Constant(0x20.into())), + &self.scalar(Value::Constant(U256::from(0x20))), + &self.scalar(Value::Constant(U256::from(0x20))), + &self.scalar(Value::Constant(U256::from(0x20))), scalar, - &self.scalar(Value::Constant(self.scalar_modulus - 2)), + &self.scalar(Value::Constant(self.scalar_modulus - U256::from(2))), &self.scalar(Value::Constant(self.scalar_modulus)), ] .map(|value| self.dup_scalar(value).ptr()); @@ -401,8 +395,8 @@ impl EvmLoader { fn add(self: &Rc, lhs: &Scalar, rhs: &Scalar) -> Scalar { if let (Value::Constant(lhs), Value::Constant(rhs)) = (&lhs.value, &rhs.value) { - let out = (U512::from(lhs) + U512::from(rhs)) % U512::from(self.scalar_modulus); - return self.scalar(Value::Constant(out.try_into().unwrap())); + let out = (U512::from(*lhs) + U512::from(*rhs)) % U512::from(self.scalar_modulus); + return self.scalar(Value::Constant(U256::from(out))); } self.scalar(Value::Sum( @@ -424,8 +418,8 @@ impl EvmLoader { fn mul(self: &Rc, lhs: &Scalar, rhs: &Scalar) -> Scalar { if let (Value::Constant(lhs), Value::Constant(rhs)) = (&lhs.value, &rhs.value) { - let out = (U512::from(lhs) * U512::from(rhs)) % U512::from(self.scalar_modulus); - return self.scalar(Value::Constant(out.try_into().unwrap())); + let out = (U512::from(*lhs) * U512::from(*rhs)) % U512::from(self.scalar_modulus); + return self.scalar(Value::Constant(U256::from(out))); } self.scalar(Value::Product( @@ -445,26 +439,16 @@ impl EvmLoader { #[cfg(test)] impl EvmLoader { - fn start_gas_metering(self: &Rc, identifier: &str) { - self.gas_metering_ids - .borrow_mut() - .push(identifier.to_string()); - let code = format!("let {identifier} := gas()"); - self.code.borrow_mut().runtime_append(code); + fn start_gas_metering(self: &Rc, _: &str) { + // unimplemented } fn end_gas_metering(self: &Rc) { - let code = format!( - "log1(0, 0, sub({}, gas()))", - self.gas_metering_ids.borrow().last().unwrap() - ); - self.code.borrow_mut().runtime_append(code); + // unimplemented } - pub fn print_gas_metering(self: &Rc, costs: Vec) { - for (identifier, cost) in self.gas_metering_ids.borrow().iter().zip(costs) { - println!("{}: {}", identifier, cost); - } + pub fn print_gas_metering(self: &Rc, _: Vec) { + // unimplemented } } @@ -681,7 +665,7 @@ where fn ec_point_load_const(&self, value: &C) -> EcPoint { let coordinates = value.coordinates().unwrap(); let [x, y] = [coordinates.x(), coordinates.y()] - .map(|coordinate| U256::from_little_endian(coordinate.to_repr().as_ref())); + .map(|coordinate| U256::try_from_le_slice(coordinate.to_repr().as_ref()).unwrap()); self.ec_point(Value::Constant((x, y))) } @@ -696,7 +680,7 @@ where .iter() .cloned() .map(|(scalar, ec_point)| match scalar.value { - Value::Constant(constant) if U256::one() == constant => ec_point.clone(), + Value::Constant(constant) if U256::from(1) == constant => ec_point.clone(), _ => ec_point.loader.ec_point_scalar_mul(ec_point, scalar), }) .reduce(|acc, ec_point| acc.loader.ec_point_add(&acc, &ec_point)) diff --git a/snark-verifier/src/loader/evm/test.rs b/snark-verifier/src/loader/evm/test.rs deleted file mode 100644 index e3467408..00000000 --- a/snark-verifier/src/loader/evm/test.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - loader::evm::{test::tui::Tui, Address, ExecutorBuilder, U256}, - util::Itertools, -}; -use std::env::var_os; - -mod tui; - -fn debug() -> bool { - matches!( - var_os("DEBUG"), - Some(value) if value.to_str() == Some("1") - ) -} - -pub fn execute(deployment_code: Vec, calldata: Vec) -> (bool, u64, Vec) { - assert!( - deployment_code.len() <= 0x6000, - "Contract size {} exceeds the limit 24576", - deployment_code.len() - ); - - let debug = debug(); - let caller = Address::from_low_u64_be(0xfe); - - let mut evm = ExecutorBuilder::default() - .with_gas_limit(u64::MAX.into()) - .set_debugger(debug) - .build(); - - let contract = evm - .deploy(caller, deployment_code.into(), 0.into()) - .address - .unwrap(); - let result = evm.call_raw(caller, contract, calldata.into(), 0.into()); - - let costs = result - .logs - .into_iter() - .map(|log| U256::from_big_endian(log.topics[0].as_bytes()).as_u64()) - .collect_vec(); - - if debug { - Tui::new(result.debug.unwrap().flatten(0), 0).start(); - } - - (!result.reverted, result.gas_used, costs) -} diff --git a/snark-verifier/src/loader/evm/test/tui.rs b/snark-verifier/src/loader/evm/test/tui.rs deleted file mode 100644 index 455f308b..00000000 --- a/snark-verifier/src/loader/evm/test/tui.rs +++ /dev/null @@ -1,943 +0,0 @@ -//! Copied and modified from https://github.com/foundry-rs/foundry/blob/master/ui/src/lib.rs - -use crate::loader::evm::{ - util::executor::{CallKind, DebugStep}, - Address, -}; -use crossterm::{ - event::{ - self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, - MouseEvent, MouseEventKind, - }, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; -use revm::opcode; -use std::{ - cmp::{max, min}, - io, - sync::mpsc, - thread, - time::{Duration, Instant}, -}; -use tui::{ - backend::{Backend, CrosstermBackend}, - layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::{Color, Modifier, Style}, - terminal::Frame, - text::{Span, Spans, Text}, - widgets::{Block, Borders, Paragraph, Wrap}, - Terminal, -}; - -pub struct Tui { - debug_arena: Vec<(Address, Vec, CallKind)>, - terminal: Terminal>, - key_buffer: String, - current_step: usize, -} - -impl Tui { - pub fn new(debug_arena: Vec<(Address, Vec, CallKind)>, current_step: usize) -> Self { - enable_raw_mode().unwrap(); - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap(); - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); - terminal.hide_cursor().unwrap(); - Tui { - debug_arena, - terminal, - key_buffer: String::new(), - current_step, - } - } - - pub fn start(mut self) { - std::panic::set_hook(Box::new(|e| { - disable_raw_mode().expect("Unable to disable raw mode"); - execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture) - .expect("unable to execute disable mouse capture"); - println!("{e}"); - })); - let tick_rate = Duration::from_millis(60); - - let (tx, rx) = mpsc::channel(); - thread::spawn(move || { - let mut last_tick = Instant::now(); - loop { - if event::poll(tick_rate - last_tick.elapsed()).unwrap() { - let event = event::read().unwrap(); - if let Event::Key(key) = event { - if tx.send(Interrupt::KeyPressed(key)).is_err() { - return; - } - } else if let Event::Mouse(mouse) = event { - if tx.send(Interrupt::MouseEvent(mouse)).is_err() { - return; - } - } - } - if last_tick.elapsed() > tick_rate { - if tx.send(Interrupt::IntervalElapsed).is_err() { - return; - } - last_tick = Instant::now(); - } - } - }); - - self.terminal.clear().unwrap(); - let mut draw_memory: DrawMemory = DrawMemory::default(); - - let debug_call = &self.debug_arena; - let mut opcode_list: Vec = debug_call[0] - .1 - .iter() - .map(|step| step.pretty_opcode()) - .collect(); - let mut last_index = 0; - - let mut stack_labels = false; - let mut mem_utf = false; - loop { - if last_index != draw_memory.inner_call_index { - opcode_list = debug_call[draw_memory.inner_call_index] - .1 - .iter() - .map(|step| step.pretty_opcode()) - .collect(); - last_index = draw_memory.inner_call_index; - } - match rx.recv().unwrap() { - Interrupt::KeyPressed(event) => match event.code { - KeyCode::Char('q') => { - disable_raw_mode().unwrap(); - execute!( - self.terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - ) - .unwrap(); - return; - } - KeyCode::Char('j') | KeyCode::Down => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - let max_mem = (debug_call[draw_memory.inner_call_index].1 - [self.current_step] - .memory - .len() - / 32) - .saturating_sub(1); - let step = if event.modifiers.contains(KeyModifiers::ALT) { - 20 - } else { - 1 - }; - if draw_memory.current_mem_startline + step < max_mem { - draw_memory.current_mem_startline += step; - } - } else if self.current_step < opcode_list.len() - 1 { - self.current_step += 1; - } else if draw_memory.inner_call_index < debug_call.len() - 1 { - draw_memory.inner_call_index += 1; - self.current_step = 0; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('J') => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - let max_stack = debug_call[draw_memory.inner_call_index].1 - [self.current_step] - .stack - .len() - .saturating_sub(1); - if draw_memory.current_stack_startline < max_stack { - draw_memory.current_stack_startline += 1; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('k') | KeyCode::Up => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - if event.modifiers.contains(KeyModifiers::CONTROL) { - draw_memory.current_mem_startline = - draw_memory.current_mem_startline.saturating_sub(1); - } else if self.current_step > 0 { - self.current_step -= 1; - } else if draw_memory.inner_call_index > 0 { - draw_memory.inner_call_index -= 1; - self.current_step = - debug_call[draw_memory.inner_call_index].1.len() - 1; - } - } - self.key_buffer.clear(); - } - KeyCode::Char('K') => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - draw_memory.current_stack_startline = - draw_memory.current_stack_startline.saturating_sub(1); - } - self.key_buffer.clear(); - } - KeyCode::Char('g') => { - draw_memory.inner_call_index = 0; - self.current_step = 0; - self.key_buffer.clear(); - } - KeyCode::Char('G') => { - draw_memory.inner_call_index = debug_call.len() - 1; - self.current_step = debug_call[draw_memory.inner_call_index].1.len() - 1; - self.key_buffer.clear(); - } - KeyCode::Char('c') => { - draw_memory.inner_call_index = - draw_memory.inner_call_index.saturating_sub(1); - self.current_step = debug_call[draw_memory.inner_call_index].1.len() - 1; - self.key_buffer.clear(); - } - KeyCode::Char('C') => { - if debug_call.len() > draw_memory.inner_call_index + 1 { - draw_memory.inner_call_index += 1; - self.current_step = 0; - } - self.key_buffer.clear(); - } - KeyCode::Char('s') => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - let remaining_ops = &opcode_list[self.current_step..]; - self.current_step += remaining_ops - .iter() - .enumerate() - .find_map(|(i, op)| { - if i < remaining_ops.len() - 1 { - match ( - op.contains("JUMP") && op != "JUMPDEST", - &*remaining_ops[i + 1], - ) { - (true, "JUMPDEST") => Some(i + 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or(opcode_list.len() - 1); - if self.current_step > opcode_list.len() { - self.current_step = opcode_list.len() - 1 - }; - } - self.key_buffer.clear(); - } - KeyCode::Char('a') => { - for _ in 0..Tui::buffer_as_number(&self.key_buffer, 1) { - let prev_ops = &opcode_list[..self.current_step]; - self.current_step = prev_ops - .iter() - .enumerate() - .rev() - .find_map(|(i, op)| { - if i > 0 { - match ( - prev_ops[i - 1].contains("JUMP") - && prev_ops[i - 1] != "JUMPDEST", - &**op, - ) { - (true, "JUMPDEST") => Some(i - 1), - _ => None, - } - } else { - None - } - }) - .unwrap_or_default(); - } - self.key_buffer.clear(); - } - KeyCode::Char('t') => { - stack_labels = !stack_labels; - } - KeyCode::Char('m') => { - mem_utf = !mem_utf; - } - KeyCode::Char(other) => match other { - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - self.key_buffer.push(other); - } - _ => { - self.key_buffer.clear(); - } - }, - _ => { - self.key_buffer.clear(); - } - }, - Interrupt::MouseEvent(event) => match event.kind { - MouseEventKind::ScrollUp => { - if self.current_step > 0 { - self.current_step -= 1; - } else if draw_memory.inner_call_index > 0 { - draw_memory.inner_call_index -= 1; - draw_memory.current_mem_startline = 0; - draw_memory.current_stack_startline = 0; - self.current_step = - debug_call[draw_memory.inner_call_index].1.len() - 1; - } - } - MouseEventKind::ScrollDown => { - if self.current_step < opcode_list.len() - 1 { - self.current_step += 1; - } else if draw_memory.inner_call_index < debug_call.len() - 1 { - draw_memory.inner_call_index += 1; - draw_memory.current_mem_startline = 0; - draw_memory.current_stack_startline = 0; - self.current_step = 0; - } - } - _ => {} - }, - Interrupt::IntervalElapsed => {} - } - let current_step = self.current_step; - self.terminal - .draw(|f| { - Tui::draw_layout( - f, - debug_call[draw_memory.inner_call_index].0, - &debug_call[draw_memory.inner_call_index].1[..], - &opcode_list, - current_step, - &mut draw_memory, - stack_labels, - mem_utf, - ) - }) - .unwrap(); - } - } - - fn buffer_as_number(buffer: &str, default_value: usize) -> usize { - if let Ok(num) = buffer.parse() { - if num >= 1 { - num - } else { - default_value - } - } else { - default_value - } - } - - fn draw_layout( - f: &mut Frame, - address: Address, - debug_steps: &[DebugStep], - opcode_list: &[String], - current_step: usize, - draw_memory: &mut DrawMemory, - stack_labels: bool, - mem_utf: bool, - ) { - let total_size = f.size(); - if total_size.width < 225 { - Tui::vertical_layout( - f, - address, - debug_steps, - opcode_list, - current_step, - draw_memory, - stack_labels, - mem_utf, - ); - } else { - Tui::square_layout( - f, - address, - debug_steps, - opcode_list, - current_step, - draw_memory, - stack_labels, - mem_utf, - ); - } - } - - fn vertical_layout( - f: &mut Frame, - address: Address, - debug_steps: &[DebugStep], - opcode_list: &[String], - current_step: usize, - draw_memory: &mut DrawMemory, - stack_labels: bool, - mem_utf: bool, - ) { - let total_size = f.size(); - if let [app, footer] = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(98, 100), Constraint::Ratio(2, 100)].as_ref()) - .split(total_size)[..] - { - if let [op_pane, stack_pane, memory_pane] = Layout::default() - .direction(Direction::Vertical) - .constraints( - [ - Constraint::Ratio(1, 3), - Constraint::Ratio(1, 3), - Constraint::Ratio(1, 3), - ] - .as_ref(), - ) - .split(app)[..] - { - Tui::draw_footer(f, footer); - Tui::draw_op_list( - f, - address, - debug_steps, - opcode_list, - current_step, - draw_memory, - op_pane, - ); - Tui::draw_stack( - f, - debug_steps, - current_step, - stack_pane, - stack_labels, - draw_memory, - ); - Tui::draw_memory( - f, - debug_steps, - current_step, - memory_pane, - mem_utf, - draw_memory, - ); - } else { - panic!("unable to create vertical panes") - } - } else { - panic!("unable to create footer / app") - } - } - - fn square_layout( - f: &mut Frame, - address: Address, - debug_steps: &[DebugStep], - opcode_list: &[String], - current_step: usize, - draw_memory: &mut DrawMemory, - stack_labels: bool, - mem_utf: bool, - ) { - let total_size = f.size(); - - if let [app, footer] = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(98, 100), Constraint::Ratio(2, 100)].as_ref()) - .split(total_size)[..] - { - if let [left_pane, right_pane] = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)].as_ref()) - .split(app)[..] - { - if let [stack_pane, memory_pane] = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(2, 5), Constraint::Ratio(3, 5)].as_ref()) - .split(right_pane)[..] - { - Tui::draw_footer(f, footer); - Tui::draw_op_list( - f, - address, - debug_steps, - opcode_list, - current_step, - draw_memory, - left_pane, - ); - Tui::draw_stack( - f, - debug_steps, - current_step, - stack_pane, - stack_labels, - draw_memory, - ); - Tui::draw_memory( - f, - debug_steps, - current_step, - memory_pane, - mem_utf, - draw_memory, - ); - } else { - panic!("Couldn't generate horizontal split layout 1:2."); - } - } else { - panic!("Couldn't generate vertical split layout 1:2."); - } - } else { - panic!("Couldn't generate application & footer") - } - } - - fn draw_footer(f: &mut Frame, area: Rect) { - let block_controls = Block::default(); - - let text_output = Text::from(Span::styled( - "[q]: quit | [k/j]: prev/next op | [a/s]: prev/next jump | [c/C]: prev/next call | [g/G]: start/end | [t]: toggle stack labels | [m]: toggle memory decoding | [shift + j/k]: scroll stack | [ctrl + j/k]: scroll memory", - Style::default().add_modifier(Modifier::DIM) - )); - let paragraph = Paragraph::new(text_output) - .block(block_controls) - .alignment(Alignment::Center) - .wrap(Wrap { trim: false }); - f.render_widget(paragraph, area); - } - - fn draw_op_list( - f: &mut Frame, - address: Address, - debug_steps: &[DebugStep], - opcode_list: &[String], - current_step: usize, - draw_memory: &mut DrawMemory, - area: Rect, - ) { - let block_source_code = Block::default() - .title(format!( - "Address: {:?} | PC: {} | Gas used in call: {}", - address, - if let Some(step) = debug_steps.get(current_step) { - step.pc.to_string() - } else { - "END".to_string() - }, - debug_steps[current_step].total_gas_used, - )) - .borders(Borders::ALL); - let mut text_output: Vec = Vec::new(); - - let display_start; - - let height = area.height as i32; - let extra_top_lines = height / 2; - let prev_start = draw_memory.current_startline; - let abs_min_start = 0; - let abs_max_start = (opcode_list.len() as i32 - 1) - (height / 2); - let mut min_start = max( - current_step as i32 - height + extra_top_lines, - abs_min_start, - ) as usize; - - let mut max_start = max( - min(current_step as i32 - extra_top_lines, abs_max_start), - abs_min_start, - ) as usize; - - if min_start > max_start { - std::mem::swap(&mut min_start, &mut max_start); - } - - if prev_start < min_start { - display_start = min_start; - } else if prev_start > max_start { - display_start = max_start; - } else { - display_start = prev_start; - } - draw_memory.current_startline = display_start; - - let max_pc_len = debug_steps - .iter() - .fold(0, |max_val, val| val.pc.max(max_val)) - .to_string() - .len(); - - let mut add_new_line = |line_number| { - let bg_color = if line_number == current_step { - Color::DarkGray - } else { - Color::Reset - }; - - let line_number_format = if line_number == current_step { - let step: &DebugStep = &debug_steps[line_number]; - format!("{:0>max_pc_len$x}|▶", step.pc, max_pc_len = max_pc_len) - } else if line_number < debug_steps.len() { - let step: &DebugStep = &debug_steps[line_number]; - format!("{:0>max_pc_len$x}| ", step.pc, max_pc_len = max_pc_len) - } else { - "END CALL".to_string() - }; - - if let Some(op) = opcode_list.get(line_number) { - text_output.push(Spans::from(Span::styled( - format!("{line_number_format}{op}"), - Style::default().fg(Color::White).bg(bg_color), - ))); - } else { - text_output.push(Spans::from(Span::styled( - line_number_format, - Style::default().fg(Color::White).bg(bg_color), - ))); - } - }; - for number in display_start..opcode_list.len() { - add_new_line(number); - } - add_new_line(opcode_list.len()); - let paragraph = Paragraph::new(text_output) - .block(block_source_code) - .wrap(Wrap { trim: true }); - f.render_widget(paragraph, area); - } - - fn draw_stack( - f: &mut Frame, - debug_steps: &[DebugStep], - current_step: usize, - area: Rect, - stack_labels: bool, - draw_memory: &mut DrawMemory, - ) { - let stack = &debug_steps[current_step].stack; - let stack_space = Block::default() - .title(format!("Stack: {}", stack.len())) - .borders(Borders::ALL); - let min_len = usize::max(format!("{}", stack.len()).len(), 2); - - let indices_affected = stack_indices_affected(debug_steps[current_step].instruction.0); - - let text: Vec = stack - .iter() - .rev() - .enumerate() - .skip(draw_memory.current_stack_startline) - .map(|(i, stack_item)| { - let affected = indices_affected - .iter() - .find(|(affected_index, _name)| *affected_index == i); - - let mut words: Vec = (0..32) - .into_iter() - .rev() - .map(|i| stack_item.byte(i)) - .map(|byte| { - Span::styled( - format!("{:02x} ", byte), - if affected.is_some() { - Style::default().fg(Color::Cyan) - } else if byte == 0 { - Style::default().add_modifier(Modifier::DIM) - } else { - Style::default().fg(Color::White) - }, - ) - }) - .collect(); - - if stack_labels { - if let Some((_, name)) = affected { - words.push(Span::raw(format!("| {name}"))); - } else { - words.push(Span::raw("| ".to_string())); - } - } - - let mut spans = vec![Span::styled( - format!("{:0min_len$}| ", i, min_len = min_len), - Style::default().fg(Color::White), - )]; - spans.extend(words); - spans.push(Span::raw("\n")); - - Spans::from(spans) - }) - .collect(); - - let paragraph = Paragraph::new(text) - .block(stack_space) - .wrap(Wrap { trim: true }); - f.render_widget(paragraph, area); - } - - fn draw_memory( - f: &mut Frame, - debug_steps: &[DebugStep], - current_step: usize, - area: Rect, - mem_utf8: bool, - draw_mem: &mut DrawMemory, - ) { - let memory = &debug_steps[current_step].memory; - let stack_space = Block::default() - .title(format!( - "Memory (max expansion: {} bytes)", - memory.effective_len() - )) - .borders(Borders::ALL); - let memory = memory.data(); - let max_i = memory.len() / 32; - let min_len = format!("{:x}", max_i * 32).len(); - - let mut word = None; - let mut color = None; - let stack_len = debug_steps[current_step].stack.len(); - if stack_len > 0 { - let w = debug_steps[current_step].stack[stack_len - 1]; - match debug_steps[current_step].instruction.0 { - opcode::MLOAD => { - word = Some(w.as_usize() / 32); - color = Some(Color::Cyan); - } - opcode::MSTORE => { - word = Some(w.as_usize() / 32); - color = Some(Color::Red); - } - _ => {} - } - } - - if current_step > 0 { - let prev_step = current_step - 1; - let stack_len = debug_steps[prev_step].stack.len(); - if debug_steps[prev_step].instruction.0 == opcode::MSTORE { - let prev_top = debug_steps[prev_step].stack[stack_len - 1]; - word = Some(prev_top.as_usize() / 32); - color = Some(Color::Green); - } - } - - let text: Vec = memory - .chunks(32) - .enumerate() - .skip(draw_mem.current_mem_startline) - .map(|(i, mem_word)| { - let words: Vec = mem_word - .iter() - .map(|byte| { - Span::styled( - format!("{:02x} ", byte), - if let (Some(w), Some(color)) = (word, color) { - if i == w { - Style::default().fg(color) - } else if *byte == 0 { - Style::default().add_modifier(Modifier::DIM) - } else { - Style::default().fg(Color::White) - } - } else if *byte == 0 { - Style::default().add_modifier(Modifier::DIM) - } else { - Style::default().fg(Color::White) - }, - ) - }) - .collect(); - - let mut spans = vec![Span::styled( - format!("{:0min_len$x}| ", i * 32, min_len = min_len), - Style::default().fg(Color::White), - )]; - spans.extend(words); - - if mem_utf8 { - let chars: Vec = mem_word - .chunks(4) - .map(|utf| { - if let Ok(utf_str) = std::str::from_utf8(utf) { - Span::raw(utf_str.replace(char::from(0), ".")) - } else { - Span::raw(".") - } - }) - .collect(); - spans.push(Span::raw("|")); - spans.extend(chars); - } - - spans.push(Span::raw("\n")); - - Spans::from(spans) - }) - .collect(); - let paragraph = Paragraph::new(text) - .block(stack_space) - .wrap(Wrap { trim: true }); - f.render_widget(paragraph, area); - } -} - -enum Interrupt { - KeyPressed(KeyEvent), - MouseEvent(MouseEvent), - IntervalElapsed, -} - -struct DrawMemory { - pub current_startline: usize, - pub inner_call_index: usize, - pub current_mem_startline: usize, - pub current_stack_startline: usize, -} - -impl DrawMemory { - fn default() -> Self { - DrawMemory { - current_startline: 0, - inner_call_index: 0, - current_mem_startline: 0, - current_stack_startline: 0, - } - } -} - -fn stack_indices_affected(op: u8) -> Vec<(usize, &'static str)> { - match op { - 0x01 => vec![(0, "a"), (1, "b")], - 0x02 => vec![(0, "a"), (1, "b")], - 0x03 => vec![(0, "a"), (1, "b")], - 0x04 => vec![(0, "a"), (1, "b")], - 0x05 => vec![(0, "a"), (1, "b")], - 0x06 => vec![(0, "a"), (1, "b")], - 0x07 => vec![(0, "a"), (1, "b")], - 0x08 => vec![(0, "a"), (1, "b"), (2, "mod")], - 0x09 => vec![(0, "a"), (1, "b"), (2, "mod")], - 0x0a => vec![(0, "base"), (1, "exp")], - 0x0b => vec![(0, "i"), (1, "a")], - 0x10 => vec![(0, "a"), (1, "b")], - 0x11 => vec![(0, "a"), (1, "b")], - 0x12 => vec![(0, "a"), (1, "b")], - 0x13 => vec![(0, "a"), (1, "b")], - 0x14 => vec![(0, "a"), (1, "b")], - 0x15 => vec![(0, "a")], - 0x16 => vec![(0, "a"), (1, "b")], - 0x17 => vec![(0, "a"), (1, "b")], - 0x18 => vec![(0, "a"), (1, "b")], - 0x19 => vec![(0, "a")], - 0x1a => vec![(0, "i"), (1, "a")], - 0x1b => vec![(0, "shift"), (1, "a")], - 0x1c => vec![(0, "shift"), (1, "a")], - 0x1d => vec![(0, "shift"), (1, "a")], - 0x20 => vec![(0, "offset"), (1, "length")], - 0x31 => vec![(0, "address")], - 0x35 => vec![(0, "offset")], - 0x37 => vec![(0, "dst"), (1, "src"), (2, "length")], - 0x39 => vec![(0, "dst"), (1, "src"), (2, "length")], - 0x3b => vec![(0, "address")], - 0x3c => vec![(0, "address"), (1, "dst"), (2, "src"), (3, "length")], - 0x3e => vec![(0, "dst"), (1, "src"), (2, "length")], - 0x3f => vec![(0, "address")], - 0x40 => vec![(0, "number")], - 0x50 => vec![(0, "a")], - 0x51 => vec![(0, "offset")], - 0x52 => vec![(0, "offset"), (1, "a")], - 0x53 => vec![(0, "offset"), (1, "a")], - 0x54 => vec![(0, "key")], - 0x55 => vec![(0, "key"), (1, "a")], - 0x56 => vec![(0, "dst")], - 0x57 => vec![(0, "dst"), (1, "cond")], - 0x80 => vec![(0, "a")], - 0x81 => vec![(1, "a")], - 0x82 => vec![(2, "a")], - 0x83 => vec![(3, "a")], - 0x84 => vec![(4, "a")], - 0x85 => vec![(5, "a")], - 0x86 => vec![(6, "a")], - 0x87 => vec![(7, "a")], - 0x88 => vec![(8, "a")], - 0x89 => vec![(9, "a")], - 0x8a => vec![(10, "a")], - 0x8b => vec![(11, "a")], - 0x8c => vec![(12, "a")], - 0x8d => vec![(13, "a")], - 0x8e => vec![(14, "a")], - 0x8f => vec![(15, "a")], - 0x90 => vec![(0, "a"), (1, "a")], - 0x91 => vec![(0, "a"), (2, "a")], - 0x92 => vec![(0, "a"), (3, "a")], - 0x93 => vec![(0, "a"), (4, "a")], - 0x94 => vec![(0, "a"), (5, "a")], - 0x95 => vec![(0, "a"), (6, "a")], - 0x96 => vec![(0, "a"), (7, "a")], - 0x97 => vec![(0, "a"), (8, "a")], - 0x98 => vec![(0, "a"), (9, "a")], - 0x99 => vec![(0, "a"), (10, "a")], - 0x9a => vec![(0, "a"), (11, "a")], - 0x9b => vec![(0, "a"), (12, "a")], - 0x9c => vec![(0, "a"), (13, "a")], - 0x9d => vec![(0, "a"), (14, "a")], - 0x9e => vec![(0, "a"), (15, "a")], - 0x9f => vec![(0, "a"), (16, "a")], - 0xa0 => vec![(0, "offset"), (1, "length")], - 0xa1 => vec![(0, "offset"), (1, "length"), (2, "topic")], - 0xa2 => vec![(0, "offset"), (1, "length"), (2, "topic1"), (3, "topic2")], - 0xa3 => vec![ - (0, "offset"), - (1, "length"), - (2, "topic1"), - (3, "topic2"), - (4, "topic3"), - ], - 0xa4 => vec![ - (0, "offset"), - (1, "length"), - (2, "topic1"), - (3, "topic2"), - (4, "topic3"), - (5, "topic4"), - ], - 0xf0 => vec![(0, "value"), (1, "offset"), (2, "length")], - 0xf1 => vec![ - (0, "gas"), - (1, "address"), - (2, "value"), - (3, "cd_offset"), - (4, "cd_length"), - (5, "rd_offset"), - (6, "rd_length"), - ], - 0xf2 => vec![ - (0, "gas"), - (1, "address"), - (2, "value"), - (3, "cd_offset"), - (4, "cd_length"), - (5, "rd_offset"), - (6, "rd_length"), - ], - 0xf3 => vec![(0, "offset"), (1, "length")], - 0xf4 => vec![ - (0, "gas"), - (1, "address"), - (2, "cd_offset"), - (3, "cd_length"), - (4, "rd_offset"), - (5, "rd_length"), - ], - 0xf5 => vec![(0, "value"), (1, "offset"), (2, "length"), (3, "salt")], - 0xfa => vec![ - (0, "gas"), - (1, "address"), - (2, "cd_offset"), - (3, "cd_length"), - (4, "rd_offset"), - (5, "rd_length"), - ], - 0xfd => vec![(0, "offset"), (1, "length")], - 0xff => vec![(0, "address")], - _ => vec![], - } -} diff --git a/snark-verifier/src/loader/evm/util.rs b/snark-verifier/src/loader/evm/util.rs index 689518da..78827cb2 100644 --- a/snark-verifier/src/loader/evm/util.rs +++ b/snark-verifier/src/loader/evm/util.rs @@ -8,12 +8,11 @@ use std::{ process::{Command, Stdio}, }; -pub use primitive_types::{H160 as Address, H256, U256, U512}; +pub use executor::deploy_and_call; +pub use revm::primitives::ruint::aliases::{B160 as Address, B256, U256, U512}; pub(crate) mod executor; -pub use executor::ExecutorBuilder; - /// Memory chunk in EVM. #[derive(Debug)] pub struct MemoryChunk { @@ -55,7 +54,7 @@ pub fn fe_to_u256(f: F) -> U256 where F: PrimeField, { - U256::from_little_endian(f.to_repr().as_ref()) + U256::from_le_bytes(f.to_repr()) } /// Convert a [`U256`] into a [`PrimeField`]. @@ -64,9 +63,7 @@ where F: PrimeField, { let value = value % modulus::(); - let mut repr = F::Repr::default(); - value.to_little_endian(repr.as_mut()); - F::from_repr(repr).unwrap() + F::from_repr(value.to_le_bytes::<32>()).unwrap() } /// Returns modulus of [`PrimeField`] as [`U256`]. @@ -74,7 +71,7 @@ pub fn modulus() -> U256 where F: PrimeField, { - U256::from_little_endian((-F::ONE).to_repr().as_ref()) + 1 + U256::from_le_bytes((-F::ONE).to_repr()) + U256::from(1) } /// Encode instances and proof into calldata. diff --git a/snark-verifier/src/loader/evm/util/executor.rs b/snark-verifier/src/loader/evm/util/executor.rs index de466c08..e2c5bb2c 100644 --- a/snark-verifier/src/loader/evm/util/executor.rs +++ b/snark-verifier/src/loader/evm/util/executor.rs @@ -1,875 +1,59 @@ -//! Copied and modified from -//! - -use crate::loader::evm::{Address, H256, U256}; -use bytes::Bytes; use revm::{ - evm_inner, opcode, spec_opcode_gas, Account, BlockEnv, CallInputs, CallScheme, CreateInputs, - CreateScheme, Database, DatabaseCommit, EVMData, Env, ExecutionResult, Gas, GasInspector, - InMemoryDB, Inspector, Interpreter, Memory, OpCode, Return, TransactOut, TransactTo, TxEnv, + primitives::{CreateScheme, ExecutionResult, Output, TransactTo, TxEnv}, + InMemoryDB, EVM, }; -use sha3::{Digest, Keccak256}; -use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; -macro_rules! return_ok { - () => { - Return::Continue | Return::Stop | Return::Return | Return::SelfDestruct +/// Deploy contract and then call with calldata. +/// Returns gas_used of call to deployed contract if both transactions are successful. +pub fn deploy_and_call(deployment_code: Vec, calldata: Vec) -> Result { + let mut evm = EVM { + env: Default::default(), + db: Some(InMemoryDB::default()), }; -} - -fn keccak256(data: impl AsRef<[u8]>) -> [u8; 32] { - Keccak256::digest(data.as_ref()).into() -} - -fn get_contract_address(sender: impl Into
, nonce: impl Into) -> Address { - let mut stream = rlp::RlpStream::new(); - stream.begin_list(2); - stream.append(&sender.into()); - stream.append(&nonce.into()); - - let hash = keccak256(&stream.out()); - - let mut bytes = [0u8; 20]; - bytes.copy_from_slice(&hash[12..]); - Address::from(bytes) -} - -fn get_create2_address( - from: impl Into
, - salt: [u8; 32], - init_code: impl Into, -) -> Address { - get_create2_address_from_hash(from, salt, keccak256(init_code.into().as_ref()).to_vec()) -} - -fn get_create2_address_from_hash( - from: impl Into
, - salt: [u8; 32], - init_code_hash: impl Into, -) -> Address { - let bytes = [ - &[0xff], - from.into().as_bytes(), - salt.as_slice(), - init_code_hash.into().as_ref(), - ] - .concat(); - - let hash = keccak256(&bytes); - - let mut bytes = [0u8; 20]; - bytes.copy_from_slice(&hash[12..]); - Address::from(bytes) -} - -fn get_create_address(call: &CreateInputs, nonce: u64) -> Address { - match call.scheme { - CreateScheme::Create => get_contract_address(call.caller, nonce), - CreateScheme::Create2 { salt } => { - let mut buffer: [u8; 4 * 8] = [0; 4 * 8]; - salt.to_big_endian(&mut buffer); - get_create2_address(call.caller, buffer, call.init_code.clone()) - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct Log { - pub address: Address, - pub topics: Vec, - pub data: Bytes, -} - -#[derive(Clone, Debug, Default)] -struct LogCollector { - logs: Vec, -} - -impl Inspector for LogCollector { - fn log(&mut self, _: &mut EVMData<'_, DB>, address: &Address, topics: &[H256], data: &Bytes) { - self.logs.push(Log { - address: *address, - topics: topics.to_vec(), - data: data.clone(), - }); - } - - fn call( - &mut self, - _: &mut EVMData<'_, DB>, - call: &mut CallInputs, - _: bool, - ) -> (Return, Gas, Bytes) { - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) - } -} - -#[derive(Clone, Debug, Copy)] -pub enum CallKind { - Call, - StaticCall, - CallCode, - DelegateCall, - Create, - Create2, -} - -impl Default for CallKind { - fn default() -> Self { - CallKind::Call - } -} - -impl From for CallKind { - fn from(scheme: CallScheme) -> Self { - match scheme { - CallScheme::Call => CallKind::Call, - CallScheme::StaticCall => CallKind::StaticCall, - CallScheme::CallCode => CallKind::CallCode, - CallScheme::DelegateCall => CallKind::DelegateCall, - } - } -} - -impl From for CallKind { - fn from(create: CreateScheme) -> Self { - match create { - CreateScheme::Create => CallKind::Create, - CreateScheme::Create2 { .. } => CallKind::Create2, - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct DebugArena { - pub arena: Vec, -} - -impl DebugArena { - fn push_node(&mut self, mut new_node: DebugNode) -> usize { - fn recursively_push( - arena: &mut Vec, - entry: usize, - mut new_node: DebugNode, - ) -> usize { - match new_node.depth { - _ if arena[entry].depth == new_node.depth - 1 => { - let id = arena.len(); - new_node.location = arena[entry].children.len(); - new_node.parent = Some(entry); - arena[entry].children.push(id); - arena.push(new_node); - id - } - _ => { - let child = *arena[entry].children.last().unwrap(); - recursively_push(arena, child, new_node) - } - } - } - - if self.arena.is_empty() { - self.arena.push(new_node); - 0 - } else if new_node.depth == 0 { - let id = self.arena.len(); - new_node.location = self.arena[0].children.len(); - new_node.parent = Some(0); - self.arena[0].children.push(id); - self.arena.push(new_node); - id - } else { - recursively_push(&mut self.arena, 0, new_node) - } - } - - #[cfg(test)] - pub fn flatten(&self, entry: usize) -> Vec<(Address, Vec, CallKind)> { - let node = &self.arena[entry]; - - let mut flattened = vec![]; - if !node.steps.is_empty() { - flattened.push((node.address, node.steps.clone(), node.kind)); - } - flattened.extend(node.children.iter().flat_map(|child| self.flatten(*child))); - - flattened - } -} - -#[derive(Clone, Debug, Default)] -pub struct DebugNode { - pub parent: Option, - pub children: Vec, - pub location: usize, - pub address: Address, - pub kind: CallKind, - pub depth: usize, - pub steps: Vec, -} - -#[derive(Clone, Debug)] -pub struct DebugStep { - pub stack: Vec, - pub memory: Memory, - pub instruction: Instruction, - pub push_bytes: Option>, - pub pc: usize, - pub total_gas_used: u64, -} - -impl Default for DebugStep { - fn default() -> Self { - Self { - stack: vec![], - memory: Memory::new(), - instruction: Instruction(revm::opcode::INVALID), - push_bytes: None, - pc: 0, - total_gas_used: 0, - } - } -} - -impl DebugStep { - #[cfg(test)] - pub fn pretty_opcode(&self) -> String { - if let Some(push_bytes) = &self.push_bytes { - format!("{}(0x{})", self.instruction, hex::encode(push_bytes)) - } else { - self.instruction.to_string() - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct Instruction(pub u8); - -impl From for Instruction { - fn from(op: u8) -> Instruction { - Instruction(op) - } -} - -impl Display for Instruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - OpCode::try_from_u8(self.0).map_or_else( - || format!("UNDEFINED(0x{:02x})", self.0), - |opcode| opcode.as_str().to_string(), - ) - ) - } -} - -#[derive(Clone, Debug)] -struct Debugger { - arena: DebugArena, - head: usize, - context: Address, - gas_inspector: Rc>, -} - -impl Debugger { - fn new(gas_inspector: Rc>) -> Self { - Self { - arena: Default::default(), - head: Default::default(), - context: Default::default(), - gas_inspector, - } - } - - fn enter(&mut self, depth: usize, address: Address, kind: CallKind) { - self.context = address; - self.head = self.arena.push_node(DebugNode { - depth, - address, - kind, - ..Default::default() - }); - } - - fn exit(&mut self) { - if let Some(parent_id) = self.arena.arena[self.head].parent { - let DebugNode { - depth, - address, - kind, - .. - } = self.arena.arena[parent_id]; - self.context = address; - self.head = self.arena.push_node(DebugNode { - depth, - address, - kind, - ..Default::default() - }); - } - } -} - -impl Inspector for Debugger { - fn step( - &mut self, - interpreter: &mut Interpreter, - data: &mut EVMData<'_, DB>, - _is_static: bool, - ) -> Return { - let pc = interpreter.program_counter(); - let op = interpreter.contract.bytecode.bytecode()[pc]; - - let opcode_infos = spec_opcode_gas(data.env.cfg.spec_id); - let opcode_info = &opcode_infos[op as usize]; - - let push_size = if opcode_info.is_push() { - (op - opcode::PUSH1 + 1) as usize - } else { - 0 - }; - let push_bytes = match push_size { - 0 => None, - n => { - let start = pc + 1; - let end = start + n; - Some(interpreter.contract.bytecode.bytecode()[start..end].to_vec()) - } - }; - - let spent = interpreter.gas.limit() - self.gas_inspector.borrow().gas_remaining(); - let total_gas_used = spent - (interpreter.gas.refunded() as u64).min(spent / 5); - - self.arena.arena[self.head].steps.push(DebugStep { - pc, - stack: interpreter.stack().data().clone(), - memory: interpreter.memory.clone(), - instruction: Instruction(op), - push_bytes, - total_gas_used, - }); - - Return::Continue - } - - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - _: bool, - ) -> (Return, Gas, Bytes) { - self.enter( - data.journaled_state.depth() as usize, - call.context.code_address, - call.context.scheme.into(), - ); - - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) - } - - fn call_end( - &mut self, - _: &mut EVMData<'_, DB>, - _: &CallInputs, - gas: Gas, - status: Return, - retdata: Bytes, - _: bool, - ) -> (Return, Gas, Bytes) { - self.exit(); - (status, gas, retdata) - } - - fn create( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { - let nonce = data.journaled_state.account(call.caller).info.nonce; - self.enter( - data.journaled_state.depth() as usize, - get_create_address(call, nonce), - CallKind::Create, - ); - - ( - Return::Continue, - None, - Gas::new(call.gas_limit), - Bytes::new(), - ) - } - - fn create_end( - &mut self, - _: &mut EVMData<'_, DB>, - _: &CreateInputs, - status: Return, - address: Option
, - gas: Gas, - retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { - self.exit(); - - (status, address, gas, retdata) - } -} - -macro_rules! call_inspectors { - ($id:ident, [ $($inspector:expr),+ ], $call:block) => { - $({ - if let Some($id) = $inspector { - $call; - } - })+ - } -} - -struct InspectorData { - logs: Vec, - debug: Option, -} - -#[derive(Default)] -struct InspectorStack { - gas: Option>>, - logs: Option, - debugger: Option, -} - -impl InspectorStack { - fn collect_inspector_states(self) -> InspectorData { - InspectorData { - logs: self.logs.map(|logs| logs.logs).unwrap_or_default(), - debug: self.debugger.map(|debugger| debugger.arena), - } - } -} - -impl Inspector for InspectorStack { - fn initialize_interp( - &mut self, - interpreter: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let status = inspector.initialize_interp(interpreter, data, is_static); - - if status != Return::Continue { - return status; - } - } - ); - - Return::Continue - } - - fn step( - &mut self, - interpreter: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let status = inspector.step(interpreter, data, is_static); - - if status != Return::Continue { - return status; - } - } - ); - - Return::Continue - } - - fn log( - &mut self, - evm_data: &mut EVMData<'_, DB>, - address: &Address, - topics: &[H256], - data: &Bytes, - ) { - call_inspectors!(inspector, [&mut self.logs], { - inspector.log(evm_data, address, topics, data); - }); - } - - fn step_end( - &mut self, - interpreter: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - status: Return, - ) -> Return { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let status = inspector.step_end(interpreter, data, is_static, status); - - if status != Return::Continue { - return status; - } - } - ); - - Return::Continue - } - - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - is_static: bool, - ) -> (Return, Gas, Bytes) { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let (status, gas, retdata) = inspector.call(data, call, is_static); - - if status != Return::Continue { - return (status, gas, retdata); - } - } - ); - - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - call: &CallInputs, - remaining_gas: Gas, - status: Return, - retdata: Bytes, - is_static: bool, - ) -> (Return, Gas, Bytes) { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let (new_status, new_gas, new_retdata) = inspector.call_end( - data, - call, - remaining_gas, - status, - retdata.clone(), - is_static, - ); - - if new_status != status || (new_status == Return::Revert && new_retdata != retdata) - { - return (new_status, new_gas, new_retdata); - } - } - ); - - (status, remaining_gas, retdata) - } - - fn create( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let (status, addr, gas, retdata) = inspector.create(data, call); - - if status != Return::Continue { - return (status, addr, gas, retdata); - } - } - ); - - ( - Return::Continue, - None, - Gas::new(call.gas_limit), - Bytes::new(), - ) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - call: &CreateInputs, - status: Return, - address: Option
, - remaining_gas: Gas, - retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { - call_inspectors!( - inspector, - [ - &mut self.gas.as_deref().map(|gas| gas.borrow_mut()), - &mut self.logs, - &mut self.debugger - ], - { - let (new_status, new_address, new_gas, new_retdata) = inspector.create_end( - data, - call, - status, - address, - remaining_gas, - retdata.clone(), - ); - - if new_status != status { - return (new_status, new_address, new_gas, new_retdata); - } - } - ); - - (status, address, remaining_gas, retdata) - } - - fn selfdestruct(&mut self) { - call_inspectors!(inspector, [&mut self.logs, &mut self.debugger], { - Inspector::::selfdestruct(inspector); - }); - } -} - -/// Call result. -#[derive(Debug)] -pub struct RawCallResult { - /// Exit reason - pub exit_reason: Return, - /// If the call is reverted or not. - pub reverted: bool, - /// Returndata - pub result: Bytes, - /// Gas used - pub gas_used: u64, - /// Gas refunded - pub gas_refunded: u64, - /// Logs emitted during the call - pub logs: Vec, - /// Debug information if any - pub debug: Option, - /// State changes if any - pub state_changeset: Option>, - /// Environment - pub env: Env, - /// Output - pub out: TransactOut, -} - -#[derive(Clone, Debug)] -pub struct DeployResult { - pub exit_reason: Return, - pub reverted: bool, - pub address: Option
, - pub gas_used: u64, - pub gas_refunded: u64, - pub logs: Vec, - pub debug: Option, - pub env: Env, -} - -/// Executor builder. -#[derive(Debug, Default)] -pub struct ExecutorBuilder { - debugger: bool, - gas_limit: Option, -} - -impl ExecutorBuilder { - /// Set `debugger`. - pub fn set_debugger(mut self, enable: bool) -> Self { - self.debugger = enable; - self - } - - /// Set `gas_limit`. - pub fn with_gas_limit(mut self, gas_limit: U256) -> Self { - self.gas_limit = Some(gas_limit); - self - } - - /// Initialize an `Executor`. - pub fn build(self) -> Executor { - Executor::new(self.debugger, self.gas_limit.unwrap_or(U256::MAX)) - } -} - -#[derive(Clone, Debug)] -pub struct Executor { - db: InMemoryDB, - debugger: bool, - gas_limit: U256, -} - -impl Executor { - fn new(debugger: bool, gas_limit: U256) -> Self { - Executor { - db: InMemoryDB::default(), - debugger, - gas_limit, - } - } - - pub fn db_mut(&mut self) -> &mut InMemoryDB { - &mut self.db - } - - pub fn deploy(&mut self, from: Address, code: Bytes, value: U256) -> DeployResult { - let env = self.build_test_env(from, TransactTo::Create(CreateScheme::Create), code, value); - let result = self.call_raw_with_env(env); - self.commit(&result); - - let RawCallResult { - exit_reason, - out, - gas_used, - gas_refunded, - logs, - debug, - env, - .. - } = result; - - let address = match (exit_reason, out) { - (return_ok!(), TransactOut::Create(_, Some(address))) => Some(address), - _ => None, - }; - - DeployResult { - exit_reason, - reverted: !matches!(exit_reason, return_ok!()), - address, - gas_used, - gas_refunded, - logs, - debug, - env, - } - } - - pub fn call_raw( - &self, - from: Address, - to: Address, - calldata: Bytes, - value: U256, - ) -> RawCallResult { - let env = self.build_test_env(from, TransactTo::Call(to), calldata, value); - self.call_raw_with_env(env) - } + evm.env.tx = TxEnv { + gas_limit: u64::MAX, + transact_to: TransactTo::Create(CreateScheme::Create), + data: deployment_code.into(), + ..Default::default() + }; - fn call_raw_with_env(&self, mut env: Env) -> RawCallResult { - let mut inspector = self.inspector(); - let result = - evm_inner::<_, true>(&mut env, &mut self.db.clone(), &mut inspector).transact(); - let (exec_result, state_changeset) = result; - let ExecutionResult { - exit_reason, - gas_refunded, - gas_used, - out, + let result = evm.transact_commit().unwrap(); + let contract = match result { + ExecutionResult::Success { + output: Output::Create(_, Some(contract)), .. - } = exec_result; - - let result = match out { - TransactOut::Call(ref data) => data.to_owned(), - _ => Bytes::default(), - }; - let InspectorData { logs, debug } = inspector.collect_inspector_states(); - - RawCallResult { - exit_reason, - reverted: !matches!(exit_reason, return_ok!()), - result, - gas_used, - gas_refunded, - logs: logs.to_vec(), - debug, - state_changeset: Some(state_changeset.into_iter().collect()), - env, - out, - } - } - - fn commit(&mut self, result: &RawCallResult) { - if let Some(state_changeset) = result.state_changeset.as_ref() { - self.db - .commit(state_changeset.clone().into_iter().collect()); - } - } + } => contract, + ExecutionResult::Revert { gas_used, output } => { + return Err(format!( + "Contract deployment transaction reverts with gas_used {gas_used} and output {:#x}", + output + )) + } + ExecutionResult::Halt { reason, gas_used } => return Err(format!( + "Contract deployment transaction halts unexpectedly with gas_used {gas_used} and reason {:?}", + reason + )), + _ => unreachable!(), + }; - fn inspector(&self) -> InspectorStack { - let mut stack = InspectorStack { - logs: Some(LogCollector::default()), - ..Default::default() - }; - if self.debugger { - let gas_inspector = Rc::new(RefCell::new(GasInspector::default())); - stack.gas = Some(gas_inspector.clone()); - stack.debugger = Some(Debugger::new(gas_inspector)); - } - stack - } + evm.env.tx = TxEnv { + gas_limit: u64::MAX, + transact_to: TransactTo::Call(contract), + data: calldata.into(), + ..Default::default() + }; - fn build_test_env( - &self, - caller: Address, - transact_to: TransactTo, - data: Bytes, - value: U256, - ) -> Env { - Env { - block: BlockEnv { - gas_limit: self.gas_limit, - ..BlockEnv::default() - }, - tx: TxEnv { - caller, - transact_to, - data, - value, - gas_limit: self.gas_limit.as_u64(), - ..TxEnv::default() - }, - ..Env::default() - } + let result = evm.transact_commit().unwrap(); + match result { + ExecutionResult::Success { gas_used, .. } => Ok(gas_used), + ExecutionResult::Revert { gas_used, output } => Err(format!( + "Contract call transaction reverts with gas_used {gas_used} and output {:#x}", + output + )), + ExecutionResult::Halt { reason, gas_used } => Err(format!( + "Contract call transaction halts unexpectedly with gas_used {gas_used} and reason {:?}", + reason + )), } } diff --git a/snark-verifier/src/pcs/kzg/accumulator.rs b/snark-verifier/src/pcs/kzg/accumulator.rs index a3fd8e42..ed0d87a4 100644 --- a/snark-verifier/src/pcs/kzg/accumulator.rs +++ b/snark-verifier/src/pcs/kzg/accumulator.rs @@ -59,7 +59,6 @@ mod native { let [lhs_x, lhs_y, rhs_x, rhs_y]: [_; 4] = limbs .chunks(LIMBS) - .into_iter() .map(|limbs| { fe_from_limbs::<_, _, LIMBS, BITS>( limbs @@ -114,7 +113,6 @@ mod evm { let [lhs_x, lhs_y, rhs_x, rhs_y]: [[_; LIMBS]; 4] = limbs .chunks(LIMBS) - .into_iter() .map(|limbs| limbs.to_vec().try_into().unwrap()) .collect_vec() .try_into() diff --git a/snark-verifier/src/pcs/kzg/decider.rs b/snark-verifier/src/pcs/kzg/decider.rs index 7258d771..085a19cd 100644 --- a/snark-verifier/src/pcs/kzg/decider.rs +++ b/snark-verifier/src/pcs/kzg/decider.rs @@ -127,10 +127,10 @@ mod evm { let x = coordinates.x().to_repr(); let y = coordinates.y().to_repr(); ( - U256::from_little_endian(&x.as_ref()[32..]), - U256::from_little_endian(&x.as_ref()[..32]), - U256::from_little_endian(&y.as_ref()[32..]), - U256::from_little_endian(&y.as_ref()[..32]), + U256::try_from_le_slice(&x.as_ref()[32..]).unwrap(), + U256::try_from_le_slice(&x.as_ref()[..32]).unwrap(), + U256::try_from_le_slice(&y.as_ref()[32..]).unwrap(), + U256::try_from_le_slice(&y.as_ref()[..32]).unwrap(), ) }); loader.pairing(&lhs, g2, &rhs, minus_s_g2); diff --git a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs index 3f1934e3..cbfa0000 100644 --- a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs +++ b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs @@ -167,8 +167,8 @@ fn query_sets(queries: &[Query]) -> Vec>( - sets: &[QuerySet<'a, F, T>], +fn query_set_coeffs>( + sets: &[QuerySet], z: &T, z_prime: &T, ) -> Vec> { diff --git a/snark-verifier/src/system/halo2.rs b/snark-verifier/src/system/halo2.rs index 90c72a49..6819e42d 100644 --- a/snark-verifier/src/system/halo2.rs +++ b/snark-verifier/src/system/halo2.rs @@ -639,9 +639,9 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { .iter() .zip( iter::successors( - Some(F::DELTA.pow_vartime(&[(i - * self.permutation_chunk_size) - as u64])), + Some(F::DELTA.pow_vartime([ + (i * self.permutation_chunk_size) as u64, + ])), |delta| Some(F::DELTA * delta), ) .map(Expression::Constant), diff --git a/snark-verifier/src/system/halo2/test/kzg/evm.rs b/snark-verifier/src/system/halo2/test/kzg/evm.rs index 8c44ced2..5842914d 100644 --- a/snark-verifier/src/system/halo2/test/kzg/evm.rs +++ b/snark-verifier/src/system/halo2/test/kzg/evm.rs @@ -24,7 +24,7 @@ macro_rules! halo2_kzg_evm_verify { use halo2_proofs::poly::commitment::ParamsProver; use std::rc::Rc; use $crate::{ - loader::evm::{compile_yul, encode_calldata, execute, EvmLoader}, + loader::evm::{compile_yul, deploy_and_call, encode_calldata, EvmLoader}, system::halo2::{ test::kzg::{BITS, LIMBS}, transcript::evm::EvmTranscript, @@ -51,13 +51,15 @@ macro_rules! halo2_kzg_evm_verify { compile_yul(&loader.yul_code()) }; - let (accept, total_cost, costs) = - execute(deployment_code, encode_calldata($instances, &$proof)); + let calldata = encode_calldata($instances, &$proof); + let gas_cost = deploy_and_call(deployment_code.clone(), calldata.clone()).unwrap(); + println!("Total gas cost: {}", gas_cost); - loader.print_gas_metering(costs); - println!("Total gas cost: {}", total_cost); - - assert!(accept); + let mut calldata = calldata; + calldata[0] = calldata[0].wrapping_add(1); + assert!(deploy_and_call(deployment_code, calldata) + .unwrap_err() + .starts_with("Contract call transaction reverts")) }}; } diff --git a/snark-verifier/src/system/halo2/transcript/evm.rs b/snark-verifier/src/system/halo2/transcript/evm.rs index b7e81c81..1ceeb020 100644 --- a/snark-verifier/src/system/halo2/transcript/evm.rs +++ b/snark-verifier/src/system/halo2/transcript/evm.rs @@ -189,7 +189,7 @@ where .collect_vec(); let hash: [u8; 32] = Keccak256::digest(data).into(); self.buf = hash.to_vec(); - u256_to_fe(U256::from_big_endian(hash.as_slice())) + u256_to_fe(U256::from_be_bytes(hash)) } fn common_ec_point(&mut self, ec_point: &C) -> Result<(), Error> { @@ -294,7 +294,7 @@ where type Input = [u8; 32]; fn new(challenge_input: &[u8; 32]) -> Self { - ChallengeEvm(u256_to_fe(U256::from_big_endian(challenge_input))) + ChallengeEvm(u256_to_fe(U256::from_be_bytes(*challenge_input))) } fn get_scalar(&self) -> C::Scalar { diff --git a/snark-verifier/src/util/arithmetic.rs b/snark-verifier/src/util/arithmetic.rs index 50b91b69..20c34645 100644 --- a/snark-verifier/src/util/arithmetic.rs +++ b/snark-verifier/src/util/arithmetic.rs @@ -153,8 +153,8 @@ impl Domain { pub fn rotate_scalar(&self, scalar: F, rotation: Rotation) -> F { match rotation.0.cmp(&0) { Ordering::Equal => scalar, - Ordering::Greater => scalar * self.gen.pow_vartime(&[rotation.0 as u64]), - Ordering::Less => scalar * self.gen_inv.pow_vartime(&[(-rotation.0) as u64]), + Ordering::Greater => scalar * self.gen.pow_vartime([rotation.0 as u64]), + Ordering::Less => scalar * self.gen_inv.pow_vartime([(-rotation.0) as u64]), } } } diff --git a/snark-verifier/src/util/poly.rs b/snark-verifier/src/util/poly.rs index 752b269d..14b64229 100644 --- a/snark-verifier/src/util/poly.rs +++ b/snark-verifier/src/util/poly.rs @@ -76,7 +76,7 @@ impl Polynomial { results .iter_mut() .zip(self.0.chunks(chunk_size)) - .zip(powers(x.pow_vartime(&[chunk_size as u64, 0, 0, 0]))), + .zip(powers(x.pow_vartime([chunk_size as u64]))), |((result, coeffs), scalar)| *result = evaluate_serial(coeffs) * scalar, ); results.iter().fold(F::ZERO, |acc, result| acc + result)