snark_verifier/loader/evm/
util.rs

1use crate::{
2    cost::Cost,
3    util::{arithmetic::PrimeField, Itertools},
4};
5use std::{
6    io::Write,
7    iter,
8    process::{Command, Stdio},
9};
10
11#[cfg(feature = "revm")]
12pub use executor::deploy_and_call;
13pub use ruint::aliases::{B160 as Address, B256, U256, U512};
14
15#[cfg(feature = "revm")]
16pub(crate) mod executor;
17
18/// Memory chunk in EVM.
19#[derive(Debug)]
20pub struct MemoryChunk {
21    ptr: usize,
22    len: usize,
23}
24
25impl MemoryChunk {
26    pub(crate) fn new(ptr: usize) -> Self {
27        Self { ptr, len: 0 }
28    }
29
30    pub(crate) fn ptr(&self) -> usize {
31        self.ptr
32    }
33
34    pub(crate) fn len(&self) -> usize {
35        self.len
36    }
37
38    pub(crate) fn end(&self) -> usize {
39        self.ptr + self.len
40    }
41
42    pub(crate) fn reset(&mut self, ptr: usize) {
43        self.ptr = ptr;
44        self.len = 0;
45    }
46
47    pub(crate) fn extend(&mut self, size: usize) {
48        self.len += size;
49    }
50}
51
52/// Convert a [`PrimeField`] into a [`U256`].
53/// Assuming fields that implement traits in crate `ff` always have
54/// little-endian representation.
55pub fn fe_to_u256<F>(f: F) -> U256
56where
57    F: PrimeField<Repr = [u8; 32]>,
58{
59    U256::from_le_bytes(f.to_repr())
60}
61
62/// Convert a [`U256`] into a [`PrimeField`].
63pub fn u256_to_fe<F>(value: U256) -> F
64where
65    F: PrimeField<Repr = [u8; 32]>,
66{
67    let value = value % modulus::<F>();
68    F::from_repr(value.to_le_bytes::<32>()).unwrap()
69}
70
71/// Returns modulus of [`PrimeField`] as [`U256`].
72pub fn modulus<F>() -> U256
73where
74    F: PrimeField<Repr = [u8; 32]>,
75{
76    U256::from_le_bytes((-F::ONE).to_repr()) + U256::from(1)
77}
78
79/// Encode instances and proof into calldata.
80pub fn encode_calldata<F>(instances: &[Vec<F>], proof: &[u8]) -> Vec<u8>
81where
82    F: PrimeField<Repr = [u8; 32]>,
83{
84    iter::empty()
85        .chain(
86            instances
87                .iter()
88                .flatten()
89                .flat_map(|value| value.to_repr().as_ref().iter().rev().cloned().collect_vec()),
90        )
91        .chain(proof.iter().cloned())
92        .collect()
93}
94
95/// Estimate gas cost with given [`Cost`].
96pub fn estimate_gas(cost: Cost) -> usize {
97    let proof_size = cost.num_commitment * 64 + (cost.num_evaluation + cost.num_instance) * 32;
98
99    let intrinsic_cost = 21000;
100    let calldata_cost = (proof_size as f64 * 15.25).ceil() as usize;
101    let ec_operation_cost = (45100 + cost.num_pairing * 34000) + (cost.num_msm - 2) * 6350;
102
103    intrinsic_cost + calldata_cost + ec_operation_cost
104}
105
106/// Compile given Solidity `code` into deployment bytecode.
107pub fn compile_solidity(code: &str) -> Vec<u8> {
108    let mut cmd = Command::new("solc")
109        .stdin(Stdio::piped())
110        .stdout(Stdio::piped())
111        .arg("--bin")
112        .arg("-")
113        .spawn()
114        .unwrap();
115    cmd.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();
116    let output = cmd.wait_with_output().unwrap().stdout;
117    let binary = *split_by_ascii_whitespace(&output).last().unwrap();
118    assert!(!binary.is_empty());
119    hex::decode(binary).unwrap()
120}
121
122fn split_by_ascii_whitespace(bytes: &[u8]) -> Vec<&[u8]> {
123    let mut split = Vec::new();
124    let mut start = None;
125    for (idx, byte) in bytes.iter().enumerate() {
126        if byte.is_ascii_whitespace() {
127            if let Some(start) = start.take() {
128                split.push(&bytes[start..idx]);
129            }
130        } else if start.is_none() {
131            start = Some(idx);
132        }
133    }
134    if let Some(last) = start {
135        split.push(&bytes[last..]);
136    }
137    split
138}
139
140#[test]
141fn test_split_by_ascii_whitespace_1() {
142    let bytes = b" \x01 \x02   \x03";
143    let split = split_by_ascii_whitespace(bytes);
144    assert_eq!(split, [b"\x01", b"\x02", b"\x03"]);
145}
146
147#[test]
148fn test_split_by_ascii_whitespace_2() {
149    let bytes = b"123456789abc";
150    let split = split_by_ascii_whitespace(bytes);
151    assert_eq!(split, [b"123456789abc"]);
152}