//! The contract data. pub mod ir; pub mod metadata; use std::collections::HashSet; use serde::Deserialize; use serde::Serialize; use sha3::Digest; use revive_llvm_context::PolkaVMWriteLLVM; use crate::build::contract::Contract as ContractBuild; use crate::project::Project; use crate::solc::version::Version as SolcVersion; use self::ir::IR; use self::metadata::Metadata; /// The contract data. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Contract { /// The absolute file path. pub path: String, /// The IR source code data. pub ir: IR, /// The metadata JSON. pub metadata_json: serde_json::Value, } impl Contract { /// A shortcut constructor. pub fn new( path: String, source_hash: [u8; revive_common::BYTE_LENGTH_WORD], source_version: SolcVersion, ir: IR, metadata_json: Option, ) -> Self { let metadata_json = metadata_json.unwrap_or_else(|| { serde_json::json!({ "source_hash": hex::encode(source_hash.as_slice()), "source_version": serde_json::to_value(&source_version).expect("Always valid"), }) }); Self { path, ir, metadata_json, } } /// Returns the contract identifier, which is: /// - the Yul object identifier for Yul /// - the full contract path for EVM legacy assembly /// - the module name for LLVM IR pub fn identifier(&self) -> &str { match self.ir { IR::Yul(ref yul) => yul.object.identifier.as_str(), IR::EVMLA(ref evm) => evm.assembly.full_path(), IR::LLVMIR(ref llvm_ir) => llvm_ir.path.as_str(), } } /// Extract factory dependencies. pub fn drain_factory_dependencies(&mut self) -> HashSet { match self.ir { IR::Yul(ref mut yul) => yul.object.factory_dependencies.drain().collect(), IR::EVMLA(ref mut evm) => evm.assembly.factory_dependencies.drain().collect(), IR::LLVMIR(_) => HashSet::new(), } } /// Compiles the specified contract, setting its build artifacts. pub fn compile( mut self, project: Project, optimizer_settings: revive_llvm_context::OptimizerSettings, include_metadata_hash: bool, debug_config: Option, ) -> anyhow::Result { let llvm = inkwell::context::Context::create(); let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings); let version = project.version.clone(); let identifier = self.identifier().to_owned(); let metadata = Metadata::new( self.metadata_json.take(), version.long.clone(), version.l2_revision.clone(), optimizer.settings().to_owned(), ); let metadata_json = serde_json::to_value(&metadata).expect("Always valid"); let metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]> = if include_metadata_hash { let metadata_string = serde_json::to_string(&metadata).expect("Always valid"); Some(sha3::Keccak256::digest(metadata_string.as_bytes()).into()) } else { None }; let module = match self.ir { IR::LLVMIR(ref llvm_ir) => { let memory_buffer = inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy( llvm_ir.source.as_bytes(), self.path.as_str(), ); llvm.create_module_from_ir(memory_buffer) .map_err(|error| anyhow::anyhow!(error.to_string()))? } _ => llvm.create_module(self.path.as_str()), }; let mut context = revive_llvm_context::PolkaVMContext::new( &llvm, module, optimizer, Some(project), include_metadata_hash, debug_config, ); context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default()); match self.ir { IR::Yul(_) => { context.set_yul_data(Default::default()); } IR::EVMLA(_) => { let evmla_data = revive_llvm_context::PolkaVMContextEVMLAData::new(version.default); context.set_evmla_data(evmla_data); } IR::LLVMIR(_) => {} } let factory_dependencies = self.drain_factory_dependencies(); self.ir.declare(&mut context).map_err(|error| { anyhow::anyhow!( "The contract `{}` LLVM IR generator declaration pass error: {}", self.path, error ) })?; self.ir.into_llvm(&mut context).map_err(|error| { anyhow::anyhow!( "The contract `{}` LLVM IR generator definition pass error: {}", self.path, error ) })?; let build = context.build(self.path.as_str(), metadata_hash)?; Ok(ContractBuild::new( self.path, identifier, build, metadata_json, factory_dependencies, )) } /// Get the list of missing deployable libraries. pub fn get_missing_libraries(&self) -> HashSet { self.ir.get_missing_libraries() } } impl PolkaVMWriteLLVM for Contract where D: revive_llvm_context::PolkaVMDependency + Clone, { fn declare( &mut self, context: &mut revive_llvm_context::PolkaVMContext, ) -> anyhow::Result<()> { self.ir.declare(context) } fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext) -> anyhow::Result<()> { self.ir.into_llvm(context) } }