arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/solidity-verifiers/src/evm.rs
solidity-verifiers/src/evm.rsRaw
  1pub use revm;
  2use revm::{
  3    primitives::{hex, Address, CreateScheme, ExecutionResult, Output, TransactTo, TxEnv},
  4    InMemoryDB, EVM,
  5};
  6use std::{
  7    fmt::{self, Debug, Formatter},
  8    fs::{create_dir_all, File},
  9    io::{self, Write},
 10    process::{Command, Stdio},
 11    str,
 12};
 13
 14// from: https://github.com/privacy-scaling-explorations/halo2-solidity-verifier/blob/85cb77b171ce3ee493628007c7a1cfae2ea878e6/examples/separately.rs#L56
 15pub(crate) fn save_solidity(name: impl AsRef<str>, solidity: &str) {
 16    const DIR_GENERATED: &str = "./generated";
 17    create_dir_all(DIR_GENERATED).unwrap();
 18    File::create(format!("{DIR_GENERATED}/{}", name.as_ref()))
 19        .unwrap()
 20        .write_all(solidity.as_bytes())
 21        .unwrap();
 22}
 23
 24/// Compile solidity with `--via-ir` flag, then return creation bytecode.
 25///
 26/// # Panics
 27/// Panics if executable `solc` can not be found, or compilation fails.
 28pub(crate) fn compile_solidity(solidity: impl AsRef<[u8]>, contract_name: &str) -> Vec<u8> {
 29    let mut process = match Command::new("solc")
 30        .stdin(Stdio::piped())
 31        .stdout(Stdio::piped())
 32        .stderr(Stdio::piped())
 33        .arg("--bin")
 34        .arg("--optimize")
 35        .arg("-")
 36        .spawn()
 37    {
 38        Ok(process) => process,
 39        Err(err) if err.kind() == io::ErrorKind::NotFound => {
 40            panic!("Command 'solc' not found");
 41        }
 42        Err(err) => {
 43            panic!("Failed to spawn process with command 'solc':\n{err}");
 44        }
 45    };
 46    process
 47        .stdin
 48        .take()
 49        .unwrap()
 50        .write_all(solidity.as_ref())
 51        .unwrap();
 52    let output = process.wait_with_output().unwrap();
 53    let stdout = str::from_utf8(&output.stdout).unwrap();
 54    if let Some(binary) = find_binary(stdout, contract_name) {
 55        binary
 56    } else {
 57        panic!(
 58            "Compilation fails:\n{}",
 59            str::from_utf8(&output.stderr).unwrap()
 60        )
 61    }
 62}
 63
 64/// Find binary from `stdout` with given `contract_name`.
 65/// `contract_name` is provided since `solc` may compile multiple contracts or libraries.
 66/// hence, we need to find the correct binary.
 67fn find_binary(stdout: &str, contract_name: &str) -> Option<Vec<u8>> {
 68    let start_contract = stdout.find(contract_name)?;
 69    let stdout_contract = &stdout[start_contract..];
 70    let start = stdout_contract.find("Binary:")? + 8;
 71    Some(hex::decode(&stdout_contract[start..stdout_contract.len() - 1]).unwrap())
 72}
 73
 74/// Evm runner.
 75pub(crate) struct Evm {
 76    evm: EVM<InMemoryDB>,
 77}
 78
 79impl Debug for Evm {
 80    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 81        let mut debug_struct = f.debug_struct("Evm");
 82        debug_struct
 83            .field("env", &self.evm.env)
 84            .field("db", &self.evm.db.as_ref().unwrap())
 85            .finish()
 86    }
 87}
 88
 89impl Default for Evm {
 90    fn default() -> Self {
 91        Self {
 92            evm: EVM {
 93                env: Default::default(),
 94                db: Some(Default::default()),
 95            },
 96        }
 97    }
 98}
 99
100impl Evm {
101    /// Apply create transaction with given `bytecode` as creation bytecode.
102    /// Return created `address`.
103    ///
104    /// # Panics
105    /// Panics if execution reverts or halts unexpectedly.
106    pub(crate) fn create(&mut self, bytecode: Vec<u8>) -> Address {
107        let (_, output) = self.transact_success_or_panic(TxEnv {
108            gas_limit: u64::MAX,
109            transact_to: TransactTo::Create(CreateScheme::Create),
110            data: bytecode.into(),
111            ..Default::default()
112        });
113        match output {
114            Output::Create(_, Some(address)) => address,
115            _ => unreachable!(),
116        }
117    }
118
119    /// Apply call transaction to given `address` with `calldata`.
120    /// Returns `gas_used` and `return_data`.
121    ///
122    /// # Panics
123    /// Panics if execution reverts or halts unexpectedly.
124    pub(crate) fn call(&mut self, address: Address, calldata: Vec<u8>) -> (u64, Vec<u8>) {
125        let (gas_used, output) = self.transact_success_or_panic(TxEnv {
126            gas_limit: u64::MAX,
127            transact_to: TransactTo::Call(address),
128            data: calldata.into(),
129            ..Default::default()
130        });
131        match output {
132            Output::Call(output) => (gas_used, output.into()),
133            _ => unreachable!(),
134        }
135    }
136
137    fn transact_success_or_panic(&mut self, tx: TxEnv) -> (u64, Output) {
138        self.evm.env.tx = tx;
139        let result = self.evm.transact_commit().unwrap();
140        self.evm.env.tx = Default::default();
141        match result {
142            ExecutionResult::Success {
143                gas_used,
144                output,
145                logs,
146                ..
147            } => {
148                if !logs.is_empty() {
149                    println!("--- logs from {} ---", logs[0].address);
150                    for (log_idx, log) in logs.iter().enumerate() {
151                        println!("log#{log_idx}");
152                        for (topic_idx, topic) in log.topics.iter().enumerate() {
153                            println!("  topic{topic_idx}: {topic:?}");
154                        }
155                    }
156                    println!("--- end ---");
157                }
158                (gas_used, output)
159            }
160            ExecutionResult::Revert { gas_used, output } => (gas_used, Output::Call(output)),
161            ExecutionResult::Halt { reason, gas_used } => panic!(
162                "Transaction halts unexpectedly with gas_used {gas_used} and reason {reason:?}"
163            ),
164        }
165    }
166}