diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index e43a527..3c7e173 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -49,6 +49,7 @@ pub struct CompilerInput { } /// The generic compilation output configuration. +#[derive(Debug)] pub struct CompilerOutput { /// The solc standard JSON input. pub input: CompilerInput, diff --git a/crates/compiler/src/revive_resolc.rs b/crates/compiler/src/revive_resolc.rs index 501a2f9..c808c1c 100644 --- a/crates/compiler/src/revive_resolc.rs +++ b/crates/compiler/src/revive_resolc.rs @@ -11,6 +11,7 @@ use revive_dt_config::Arguments; use revive_solc_json_interface::SolcStandardJsonOutput; /// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode. +#[derive(Debug)] pub struct Resolc { /// Path to the `resolc` executable resolc_path: PathBuf, @@ -19,6 +20,7 @@ pub struct Resolc { impl SolidityCompiler for Resolc { type Options = Vec; + #[tracing::instrument(level = "debug", ret)] fn build( &self, input: CompilerInput, @@ -69,12 +71,13 @@ impl SolidityCompiler for Resolc { }); } - let parsed = serde_json::from_slice::(&stdout).map_err(|e| { - anyhow::anyhow!( - "failed to parse resolc JSON output: {e}\nstderr: {}", - String::from_utf8_lossy(&stderr) - ) - })?; + let mut parsed = + serde_json::from_slice::(&stdout).map_err(|e| { + anyhow::anyhow!( + "failed to parse resolc JSON output: {e}\nstderr: {}", + String::from_utf8_lossy(&stderr) + ) + })?; // Detecting if the compiler output contained errors and reporting them through logs and // errors instead of returning the compiler output that might contain errors. @@ -85,6 +88,48 @@ impl SolidityCompiler for Resolc { } } + // We need to do some post processing on the output to make it in the same format that solc + // outputs. More specifically, for each contract, the `.metadata` field should be replaced + // with the `.metadata.solc_metadata` field which contains the ABI and other information + // about the compiled contracts. We do this because we do not want any downstream logic to + // need to differentiate between which compiler is being used when extracting the ABI of the + // contracts. + if let Some(ref mut contracts) = parsed.contracts { + for (contract_path, contracts_map) in contracts.iter_mut() { + for (contract_name, contract_info) in contracts_map.iter_mut() { + let Some(metadata) = contract_info.metadata.take() else { + continue; + }; + + // Get the `solc_metadata` in the metadata of the contract. + let Some(solc_metadata) = metadata + .get("solc_metadata") + .and_then(|metadata| metadata.as_str()) + else { + tracing::error!( + contract_path, + contract_name, + metadata = serde_json::to_string(&metadata).unwrap(), + "Encountered a contract compiled with resolc that has no solc_metadata" + ); + anyhow::bail!( + "Contract {} compiled with resolc that has no solc_metadata", + contract_name + ); + }; + + // Replace the original metadata with the new solc_metadata. + contract_info.metadata = + Some(serde_json::Value::String(solc_metadata.to_string())); + } + } + } + + tracing::debug!( + output = %serde_json::to_string(&parsed).unwrap(), + "Compiled successfully" + ); + Ok(CompilerOutput { input, output: parsed, diff --git a/crates/compiler/src/solc.rs b/crates/compiler/src/solc.rs index 653dd33..8184b83 100644 --- a/crates/compiler/src/solc.rs +++ b/crates/compiler/src/solc.rs @@ -11,6 +11,7 @@ use revive_dt_config::Arguments; use revive_dt_solc_binaries::download_solc; use revive_solc_json_interface::SolcStandardJsonOutput; +#[derive(Debug)] pub struct Solc { solc_path: PathBuf, } @@ -18,6 +19,7 @@ pub struct Solc { impl SolidityCompiler for Solc { type Options = (); + #[tracing::instrument(level = "debug", ret)] fn build( &self, input: CompilerInput, @@ -75,6 +77,11 @@ impl SolidityCompiler for Solc { } } + tracing::debug!( + output = %String::from_utf8_lossy(&output.stdout).to_string(), + "Compiled successfully" + ); + Ok(CompilerOutput { input, output: parsed, diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index d6783af..f4d6acc 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -302,51 +302,40 @@ where std::any::type_name::() ); - if let Some(Value::String(metadata_json_str)) = &contract.metadata { - tracing::trace!( - "metadata found for contract {contract_name}, {metadata_json_str}" - ); + let Some(Value::String(metadata)) = &contract.metadata else { + tracing::error!(?contract, "Contract does not have a metadata field"); + anyhow::bail!("Contract does not have a metadata field: {contract:?}"); + }; - match serde_json::from_str::(metadata_json_str) { - Ok(metadata_json) => { - if let Some(abi_value) = - metadata_json.get("output").and_then(|o| o.get("abi")) - { - match serde_json::from_value::(abi_value.clone()) { - Ok(parsed_abi) => { - tracing::trace!( - "ABI found in metadata for contract {}", - &contract_name - ); - self.deployed_abis - .insert(contract_name.clone(), parsed_abi); - } - Err(err) => { - anyhow::bail!( - "Failed to parse ABI from metadata for contract {}: {}", - contract_name, - err - ); - } - } - } else { - anyhow::bail!( - "No ABI found in metadata for contract {}", - contract_name - ); - } - } - Err(err) => { - anyhow::bail!( - "Failed to parse metadata JSON string for contract {}: {}", - contract_name, - err - ); - } - } - } else { - anyhow::bail!("No metadata found for contract {}", contract_name); - } + // Deserialize the solc metadata into a JSON object so we can get the ABI of the + // contracts. If we fail to perform the deserialization then we return an error + // as there's no other way to handle this. + let Ok(metadata) = serde_json::from_str::(metadata) else { + tracing::error!(%metadata, "Failed to parse solc metadata into a structured value"); + anyhow::bail!( + "Failed to parse solc metadata into a structured value {metadata}" + ); + }; + + // Accessing the ABI on the solc metadata and erroring if the accessing failed + let Some(abi) = metadata.get("output").and_then(|value| value.get("abi")) + else { + tracing::error!(%metadata, "Failed to access the .output.abi field of the solc metadata"); + anyhow::bail!( + "Failed to access the .output.abi field of the solc metadata {metadata}" + ); + }; + + // Deserialize the ABI object that we got from the unstructured JSON into a + // structured ABI object and error out if we fail. + let Ok(abi) = serde_json::from_value::(abi.clone()) else { + tracing::error!(%metadata, "Failed to deserialize ABI into a structured format"); + anyhow::bail!( + "Failed to deserialize ABI into a structured format {metadata}" + ); + }; + + self.deployed_abis.insert(contract_name.clone(), abi); } } }