From 0903718f07f31abd5f47e4781278cbac73fa1735 Mon Sep 17 00:00:00 2001 From: xermicus Date: Sat, 24 Aug 2024 02:09:56 +0200 Subject: [PATCH] add compiler helpers to solidity test utils Signed-off-by: xermicus --- crates/solidity/src/test_utils.rs | 102 ++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/crates/solidity/src/test_utils.rs b/crates/solidity/src/test_utils.rs index 310003c..55a1bdb 100644 --- a/crates/solidity/src/test_utils.rs +++ b/crates/solidity/src/test_utils.rs @@ -1,8 +1,12 @@ //! Common utility used for in frontend and integration tests. use std::collections::BTreeMap; use std::collections::BTreeSet; +use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; +use std::sync::Mutex; + +use once_cell::sync::Lazy; use crate::project::Project; use crate::solc::pipeline::Pipeline as SolcPipeline; @@ -14,6 +18,16 @@ use crate::solc::standard_json::output::Output as SolcStandardJsonOutput; use crate::solc::Compiler as SolcCompiler; use crate::warning::Warning; +static PVM_BLOB_CACHE: Lazy>>> = Lazy::new(Default::default); +static EVM_BLOB_CACHE: Lazy>>> = Lazy::new(Default::default); + +#[derive(Hash, PartialEq, Eq)] +struct CachedBlob { + contract_name: String, + solc_optimizer_enabled: bool, + pipeline: SolcPipeline, +} + /// Checks if the required executables are present in `${PATH}`. fn check_dependencies() { for executable in [ @@ -260,3 +274,91 @@ pub fn check_solidity_warning( Ok(contains_warning) } + +/// Compile the blob of `contract_name` found in given `source_code`. +/// The `solc` optimizer will be enabled +pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec { + compile_blob_with_options(contract_name, source_code, true, SolcPipeline::Yul) +} + +/// Compile the EVM bin-runtime of `contract_name` found in given `source_code`. +/// The `solc` optimizer will be enabled +pub fn compile_evm_bin_runtime(contract_name: &str, source_code: &str) -> Vec { + let pipeline = SolcPipeline::Yul; + let solc_optimizer_enabled = true; + let id = CachedBlob { + contract_name: contract_name.to_owned(), + pipeline, + solc_optimizer_enabled, + }; + + if let Some(blob) = EVM_BLOB_CACHE.lock().unwrap().get(&id) { + return blob.clone(); + } + + let file_name = "contract.sol"; + let contracts = build_solidity_with_options_evm( + [(file_name.into(), source_code.into())].into(), + Default::default(), + None, + pipeline, + solc_optimizer_enabled, + ) + .expect("source should compile"); + let bin_runtime = &contracts + .get(contract_name) + .unwrap_or_else(|| panic!("contract '{}' didn't produce bin-runtime", contract_name)) + .object; + + let blob = hex::decode(bin_runtime).expect("bin-runtime shold be hex encoded"); + + EVM_BLOB_CACHE.lock().unwrap().insert(id, blob.clone()); + + blob +} + +/// Compile the blob of `contract_name` found in given `source_code`. +pub fn compile_blob_with_options( + contract_name: &str, + source_code: &str, + solc_optimizer_enabled: bool, + pipeline: SolcPipeline, +) -> Vec { + let id = CachedBlob { + contract_name: contract_name.to_owned(), + solc_optimizer_enabled, + pipeline, + }; + + if let Some(blob) = PVM_BLOB_CACHE.lock().unwrap().get(&id) { + return blob.clone(); + } + + let file_name = "contract.sol"; + let contracts = build_solidity_with_options( + [(file_name.into(), source_code.into())].into(), + Default::default(), + None, + pipeline, + revive_llvm_context::OptimizerSettings::cycles(), + solc_optimizer_enabled, + ) + .expect("source should compile") + .contracts + .expect("source should contain at least one contract"); + + let bytecode = contracts[file_name][contract_name] + .evm + .as_ref() + .expect("source should produce EVM output") + .bytecode + .as_ref() + .expect("source should produce assembly text") + .object + .as_str(); + let blob = hex::decode(bytecode).expect("hex encoding should always be valid"); + + PVM_BLOB_CACHE.lock().unwrap().insert(id, blob.clone()); + + blob +}