Skip to content

Commit

Permalink
update: resolc code fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Brianspha committed Nov 28, 2024
1 parent 9e55c52 commit 73ecaba
Show file tree
Hide file tree
Showing 11 changed files with 1,314 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
use std::{
borrow::{Borrow, Cow},
collections::BTreeMap,
path::Path,
};

use alloy_json_abi::{Constructor, Event, Function, JsonAbi};
use alloy_primitives::{hex, Bytes};
use foundry_compilers_artifacts::{
BytecodeObject, CompactBytecode, CompactContract, CompactContractBytecode,
CompactContractBytecodeCow, CompactDeployedBytecode, Contract, SourceFile,
};
use serde::{de::value, Deserialize, Serialize};
use serde_json::Error;
use yansi::Paint;

use crate::ArtifactOutput;

#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub struct ResolcArtifactOutput();

#[derive(Debug, Serialize, Deserialize)]
pub struct ResolcContractArtifact {
artifact: revive_solidity::SolcStandardJsonOutput,
}

impl Default for ResolcContractArtifact {
fn default() -> Self {
Self {
artifact: revive_solidity::SolcStandardJsonOutput {
contracts: None,
sources: None,
errors: None,
version: None,
long_version: None,
zk_version: None,
},
}
}
}

impl<'a> From<&'a ResolcContractArtifact> for CompactContractBytecodeCow<'a> {
fn from(value: &'a ResolcContractArtifact) -> Self {
let (standard_abi, compact_bytecode, compact_deployed_bytecode) = create_byte_code(value);

Self {
abi: Some(Cow::Owned(standard_abi)),
bytecode: Some(Cow::Owned(compact_bytecode)),
deployed_bytecode: Some(Cow::Owned(compact_deployed_bytecode)),
}
}
}

impl From<ResolcContractArtifact> for CompactContractBytecode {
fn from(value: ResolcContractArtifact) -> Self {
let (standard_abi, compact_bytecode, compact_deployed_bytecode) = create_byte_code(&value);
Self {
abi: Some(standard_abi),
bytecode: Some(compact_bytecode),
deployed_bytecode: Some(compact_deployed_bytecode),
}
}
}

impl From<ResolcContractArtifact> for CompactContract {
fn from(value: ResolcContractArtifact) -> Self {
// See https://docs.soliditylang.org/en/develop/abi-spec.html
let (standard_abi, compact_bytecode, _) = create_byte_code(&value);
Self { bin: Some(compact_bytecode.object.clone()), bin_runtime: Some(compact_bytecode.object), abi: Some(standard_abi) }
}
}

impl ArtifactOutput for ResolcArtifactOutput {
type Artifact = ResolcContractArtifact;

fn contract_to_artifact(
&self,
_file: &std::path::Path,
_name: &str,
_contract: foundry_compilers_artifacts::Contract,
_source_file: Option<&foundry_compilers_artifacts::SourceFile>,
) -> Self::Artifact {
todo!("Implement this if needed")
}

fn standalone_source_file_to_artifact(
&self,
_path: &std::path::Path,
_file: &crate::sources::VersionedSourceFile,
) -> Option<Self::Artifact> {
None
}
}

impl ResolcArtifactOutput {
pub fn resolc_contract_to_artifact(
&self,
_file: &Path,
_name: &str,
contract: Contract,
source_file: Option<&SourceFile>,
) -> ResolcContractArtifact {
/* let Contract {
abi,
metadata,
userdoc,
devdoc,
ir,
storage_layout,
transient_storage_layout,
evm,
ewasm,
ir_optimized,
ir_optimized_ast,
} = contract;
let mut output = ResolcContractArtifact::default();*/
todo!("Implement this function converting standard json to revive json");

}
}

fn create_byte_code(
value: &ResolcContractArtifact,
) -> (JsonAbi, CompactBytecode, CompactDeployedBytecode) {
let binding = value.artifact.contracts.clone().unwrap();
let parent_contract =
binding.values().last().and_then(|inner_map| inner_map.values().next()).unwrap();
let abi_array: Vec<serde_json::Value> =
serde_json::from_value(parent_contract.clone().abi.unwrap()).unwrap();
let mut standard_abi = JsonAbi {
constructor: None,
fallback: None,
receive: None,
functions: BTreeMap::new(),
events: BTreeMap::new(),
errors: BTreeMap::new(),
};

for item in abi_array {
match item["type"].as_str() {
Some("constructor") => {
standard_abi.constructor = serde_json::from_value(item).unwrap();
}
Some("fallback") => {
standard_abi.fallback = serde_json::from_value(item).unwrap();
}
Some("receive") => {
standard_abi.receive = serde_json::from_value(item).unwrap();
}
Some("function") => {
let function: Function = serde_json::from_value(item).unwrap();
standard_abi
.functions
.entry(function.name.clone())
.or_insert_with(Vec::new)
.push(function);
}
Some("event") => {
let event: Event = serde_json::from_value(item).unwrap();
standard_abi.events.entry(event.name.clone()).or_insert_with(Vec::new).push(event);
}
Some("error") => {
let error: alloy_json_abi::Error = serde_json::from_value(item).unwrap();
standard_abi.errors.entry(error.name.clone()).or_insert_with(Vec::new).push(error);
}
_ => continue,
}
}

let binding = parent_contract.evm.clone().unwrap().bytecode.unwrap();
let raw_bytecode = binding.object.as_str();
let binding = parent_contract.evm.clone().unwrap().deployed_bytecode.unwrap();
let raw_deployed_bytecode = binding.object.as_str();

let bytecode = BytecodeObject::Bytecode(Bytes::from(hex::decode(raw_bytecode).unwrap()));
let deployed_bytecode =
BytecodeObject::Bytecode(Bytes::from(hex::decode(raw_deployed_bytecode).unwrap()));

let compact_bytecode = CompactBytecode {
object: bytecode,
source_map: None,
link_references: BTreeMap::default(),
};
let compact_bytecode_deployed = CompactBytecode {
object: deployed_bytecode,
source_map: None,
link_references: BTreeMap::default(),
};
let compact_deployed_bytecode = CompactDeployedBytecode {
bytecode: Some(compact_bytecode_deployed),
immutable_references: BTreeMap::default(),
};

(standard_abi, compact_bytecode, compact_deployed_bytecode)
}
2 changes: 2 additions & 0 deletions crates/compilers/src/compile/resolc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod project;
pub mod artifact_output;
Empty file.
157 changes: 157 additions & 0 deletions crates/compilers/src/compilers/resolc/compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use foundry_compilers_artifacts::{CompilerOutput, Error, SolcLanguage};
use foundry_compilers_core::error::{Result, SolcError};
use semver::Version;
use serde::Serialize;
use std::{
path::{Path, PathBuf},
process::{Command, Output, Stdio},
str::FromStr,
};

use crate::{compilers, resolver::parse::SolData, Compiler, CompilerVersion};

use super::{ResolcInput, ResolcSettings, ResolcVersionedInput};

#[derive(Clone, Debug)]
pub struct Resolc {
pub resolc: PathBuf,
pub extra_args: Vec<String>,
}

impl Compiler for Resolc {
type Input = ResolcVersionedInput;
type CompilationError = Error;
type ParsedSource = SolData;
type Settings = ResolcSettings;
type Language = SolcLanguage;

fn available_versions(&self, _language: &Self::Language) -> Vec<CompilerVersion> {
let compiler = revive_solidity::SolcCompiler::new(
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(),
)
.unwrap();
let mut versions = Vec::new();
versions.push(CompilerVersion::Remote(compiler.version.unwrap().default));
versions
}

fn compile(
&self,
_input: &Self::Input,
) -> Result<
compilers::CompilerOutput<foundry_compilers_artifacts::Error>,
foundry_compilers_core::error::SolcError,
> {
panic!("`Compiler::compile` not supported for `Resolc`, should call Resolc::compile()");
}
}

impl Resolc {
pub fn new(path: PathBuf) -> Result<Self> {
Ok(Self { resolc: path, extra_args: Vec::new() })
}

pub fn compile(&self, input: &ResolcInput) -> Result<CompilerOutput> {
match self.compile_output::<ResolcInput>(input) {
Ok(results) => {
let output = std::str::from_utf8(&results).map_err(|_| SolcError::InvalidUtf8)?;
serde_json::from_str(output).map_err(|e| SolcError::msg(e.to_string()))
}
Err(_) => Ok(CompilerOutput::default()),
}
}

pub fn compile_output<T: Serialize>(&self, input: &ResolcInput) -> Result<Vec<u8>> {
let mut cmd = self.configure_cmd();
println!("input: {:?}\n\n", input.clone());
let mut child = cmd.spawn().map_err(|err| SolcError::io(err, &self.resolc))?;

let stdin = child.stdin.as_mut().unwrap();
serde_json::to_writer(stdin, input)?;

let output = child.wait_with_output().map_err(|err| SolcError::io(err, &self.resolc))?;

compile_output(output)
}

fn configure_cmd(&self) -> Command {
let mut cmd = Command::new(&self.resolc);
cmd.stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped());
cmd.args(&self.extra_args);
cmd.arg("--standard-json");
cmd
}

pub fn get_version_for_path(path: &Path) -> Result<Version> {
let mut cmd = Command::new(path);
cmd.arg("--version").stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped());
debug!(?cmd, "getting Resolc version");
let output = cmd.output().map_err(map_io_err(path))?;
trace!(?output);
let version = version_from_output(output)?;
debug!(%version);
Ok(version)
}
}

fn map_io_err(path: &Path) -> impl FnOnce(std::io::Error) -> SolcError + '_ {
move |err| SolcError::io(err, path)
}

fn version_from_output(output: Output) -> Result<Version> {
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
let version = stdout
.lines()
.filter(|l| !l.trim().is_empty())
.last()
.ok_or_else(|| SolcError::msg("Version not found in resolc output"))?;

version
.split_whitespace()
.find_map(|s| {
let trimmed = s.trim_start_matches('v');
Version::from_str(trimmed).ok()
})
.ok_or_else(|| SolcError::msg("Unable to retrieve version from resolc output"))
} else {
Err(SolcError::solc_output(&output))
}
}

fn compile_output(output: Output) -> Result<Vec<u8>> {
if output.status.success() {
Ok(output.stdout)
} else {
Err(SolcError::solc_output(&output))
}
}

#[cfg(test)]
mod test {
use super::*;

fn resolc_instance() -> Resolc {
Resolc::new(PathBuf::from(
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(),
))
.unwrap()
}

#[test]
fn resolc_version_works() {
Resolc::get_version_for_path(&mut PathBuf::from(
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(),
))
.unwrap();
}

#[test]
fn resolc_compile_works() {
let input = include_str!("../../../../../test-data/resolc/input/compile-input.json");
let input: ResolcInput = serde_json::from_str(input).unwrap();
let out = resolc_instance().compile(&input).unwrap();
println!("out: {:?}", out);
assert!(!out.has_error());
}
}
Loading

0 comments on commit 73ecaba

Please sign in to comment.