From bd6352d6cf777030acd08f3d40960c635ca91713 Mon Sep 17 00:00:00 2001 From: Rim Rakhimov Date: Tue, 2 Jul 2024 17:31:33 +0400 Subject: [PATCH] feat(verifier): zksolc full match support (#912) * Add basic zksync endpoints definitions. Create ZksyncCompilers struct; use it to fetch solc compilers * Add zkevm settings * Make VersionNotFound error internal String * Create a generic fetcher structs * Remove non-generic fetcher structs. Make use of generic fetcher instead of non-generic one. Finish implementation of list_compilers for zksolidity service * Implement zksolc contracts parsing * Move zksync related code into a separate module. Finish the basic contract comparison * Add verifier_alliance related definitions into verification-common lib * Implement zksolc verification * chore(libs-verification-common): bump blockscout_display_bytes to v1.1.0 * chore: bump blockscout-display-bytes to v1.1.0, solidity-metadata to v1.1.0 --- libs/verification-common/Cargo.toml | 7 + libs/verification-common/src/lib.rs | 1 + .../compilation_artifacts.rs | 62 ++ .../creation_code_artifacts.rs | 53 ++ .../src/verifier_alliance/mod.rs | 12 + .../runtime_code_artifacts.rs | 81 ++ .../verifier_alliance/verification_match.rs | 171 ++++ .../verification_match_transformations.rs | 71 ++ .../verification_match_values.rs | 46 + smart-contract-verifier/Cargo.lock | 849 ++++++++++++------ smart-contract-verifier/Cargo.toml | 4 +- .../smart-contract-verifier-proto/Cargo.toml | 6 +- .../smart-contract-verifier-proto/build.rs | 1 + .../proto/v2/api_config_http.yaml | 9 + .../proto/v2/zksync-solidity.proto | 71 ++ .../smart-contract-verifier-proto/src/lib.rs | 9 + .../v2/smart-contract-verifier.swagger.yaml | 177 +++- .../smart-contract-verifier-server/Cargo.toml | 18 +- .../smart-contract-verifier-server/src/run.rs | 35 +- .../src/services/common.rs | 72 ++ .../src/services/mod.rs | 2 + .../src/services/solidity_verifier.rs | 79 +- .../src/services/vyper_verifier.rs | 34 +- .../src/services/zksync_solidity_verifier.rs | 175 ++++ .../src/settings.rs | 82 +- .../src/types/batch_verification.rs | 6 +- .../src/types/lookup_methods.rs | 8 +- .../src/types/mod.rs | 1 + .../src/types/solidity_multi_part.rs | 11 +- .../src/types/solidity_standard_json.rs | 6 +- .../src/types/source.rs | 6 +- .../src/types/verify_response.rs | 4 +- .../src/types/vyper_multi_part.rs | 11 +- .../src/types/vyper_standard_json.rs | 6 +- .../src/types/zksolc_standard_json.rs | 46 + .../simple_standard_json.json | 31 + .../tests/zksync_integration/main.rs | 53 ++ .../tests/zksync_integration/types.rs | 213 +++++ .../zksync_integration/zksync_solidity.rs | 88 ++ .../smart-contract-verifier/Cargo.toml | 14 +- .../batch_verifier/batch_contract_verifier.rs | 35 +- .../src/batch_verifier/compilation.rs | 4 +- .../src/batch_verifier/errors.rs | 21 + .../src/batch_verifier/mod.rs | 9 +- .../src/compiler/compilers.rs | 31 +- .../src/compiler/download_cache.rs | 109 +-- .../src/compiler/fetcher.rs | 39 +- .../{list_fetcher.rs => fetcher_list.rs} | 286 ++++-- .../compiler/{s3_fetcher.rs => fetcher_s3.rs} | 61 +- ...ersions_fetcher.rs => fetcher_versions.rs} | 0 .../src/compiler/mod.rs | 23 +- .../src/compiler/version_compact.rs | 97 ++ .../{version.rs => version_detailed.rs} | 59 +- .../smart-contract-verifier/src/consts.rs | 7 + .../smart-contract-verifier/src/lib.rs | 15 +- .../src/solidity/compiler.rs | 6 +- .../src/solidity/multi_part.rs | 8 +- .../src/solidity/solc_cli.rs | 6 +- .../src/solidity/standard_json.rs | 6 +- .../src/solidity/types.rs | 2 +- .../src/solidity/validator.rs | 18 +- .../src/verifier/compiler_input.rs | 0 .../src/verifier/contract_verifier.rs | 8 +- .../src/vyper/compiler.rs | 12 +- .../src/vyper/multi_part.rs | 4 +- .../src/vyper/standard_json.rs | 4 +- .../src/vyper/types.rs | 2 +- .../src/zksync/implementation.rs | 462 ++++++++++ .../smart-contract-verifier/src/zksync/mod.rs | 4 + .../eravm_metadata_hash.rs | 11 + .../era_compiler_llvm_context/mod.rs | 3 + .../zksolc_standard_json/input/language.rs | 25 + .../zksync/zksolc_standard_json/input/mod.rs | 36 + .../input/settings/metadata.rs | 35 + .../input/settings/mod.rs | 107 +++ .../input/settings/optimizer/details.rs | 66 ++ .../input/settings/optimizer/mod.rs | 68 ++ .../input/settings/selection/file/flag.rs | 55 ++ .../input/settings/selection/file/mod.rs | 24 + .../input/settings/selection/mod.rs | 19 + .../zksolc_standard_json/input/source.rs | 46 + .../src/zksync/zksolc_standard_json/mod.rs | 6 + .../output/contract/evm/bytecode.rs | 24 + .../output/contract/evm/mod.rs | 27 + .../output/contract/mod.rs | 49 + .../zksolc_standard_json/output/error/mod.rs | 176 ++++ .../output/error/source_location.rs | 46 + .../zksync/zksolc_standard_json/output/mod.rs | 282 ++++++ .../zksolc_standard_json/output/source.rs | 17 + .../smart-contract-verifier/tests/types.rs | 4 +- 90 files changed, 4307 insertions(+), 788 deletions(-) create mode 100644 libs/verification-common/src/verifier_alliance/compilation_artifacts.rs create mode 100644 libs/verification-common/src/verifier_alliance/creation_code_artifacts.rs create mode 100644 libs/verification-common/src/verifier_alliance/mod.rs create mode 100644 libs/verification-common/src/verifier_alliance/runtime_code_artifacts.rs create mode 100644 libs/verification-common/src/verifier_alliance/verification_match.rs create mode 100644 libs/verification-common/src/verifier_alliance/verification_match_transformations.rs create mode 100644 libs/verification-common/src/verifier_alliance/verification_match_values.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-proto/proto/v2/zksync-solidity.proto create mode 100644 smart-contract-verifier/smart-contract-verifier-server/src/services/common.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-server/src/services/zksync_solidity_verifier.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-server/src/types/zksolc_standard_json.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_zksync_solidity/simple_standard_json.json create mode 100644 smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/main.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/types.rs create mode 100644 smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/zksync_solidity.rs rename smart-contract-verifier/smart-contract-verifier/src/compiler/{list_fetcher.rs => fetcher_list.rs} (53%) rename smart-contract-verifier/smart-contract-verifier/src/compiler/{s3_fetcher.rs => fetcher_s3.rs} (85%) rename smart-contract-verifier/smart-contract-verifier/src/compiler/{versions_fetcher.rs => fetcher_versions.rs} (100%) create mode 100644 smart-contract-verifier/smart-contract-verifier/src/compiler/version_compact.rs rename smart-contract-verifier/smart-contract-verifier/src/compiler/{version.rs => version_detailed.rs} (91%) create mode 100644 smart-contract-verifier/smart-contract-verifier/src/verifier/compiler_input.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/implementation.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/eravm_metadata_hash.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/language.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/metadata.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/details.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/flag.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/source.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/bytecode.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/source_location.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/mod.rs create mode 100644 smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/source.rs diff --git a/libs/verification-common/Cargo.toml b/libs/verification-common/Cargo.toml index fce03e7cc..ce796ac1b 100644 --- a/libs/verification-common/Cargo.toml +++ b/libs/verification-common/Cargo.toml @@ -6,7 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +alloy-dyn-abi = "0.7" +alloy-json-abi = "0.7" +anyhow = "1.0" +blockscout-display-bytes = "1.1.0" bytes = "1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_with = "3.8" [dev-dependencies] hex = "0.4.3" \ No newline at end of file diff --git a/libs/verification-common/src/lib.rs b/libs/verification-common/src/lib.rs index 1bdb82d26..f3f4d1396 100644 --- a/libs/verification-common/src/lib.rs +++ b/libs/verification-common/src/lib.rs @@ -1 +1,2 @@ pub mod blueprint_contracts; +pub mod verifier_alliance; diff --git a/libs/verification-common/src/verifier_alliance/compilation_artifacts.rs b/libs/verification-common/src/verifier_alliance/compilation_artifacts.rs new file mode 100644 index 000000000..ec6a57f27 --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/compilation_artifacts.rs @@ -0,0 +1,62 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub trait ToCompilationArtifacts { + fn abi(&self) -> Option { + None + } + fn devdoc(&self) -> Option { + None + } + fn userdoc(&self) -> Option { + None + } + fn storage_layout(&self) -> Option { + None + } +} + +impl ToCompilationArtifacts for &T { + fn abi(&self) -> Option { + (*self).abi() + } + fn devdoc(&self) -> Option { + (*self).devdoc() + } + fn userdoc(&self) -> Option { + (*self).userdoc() + } + fn storage_layout(&self) -> Option { + (*self).storage_layout() + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CompilationArtifacts { + #[serde(skip_serializing_if = "Option::is_none")] + pub abi: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub devdoc: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub userdoc: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub storage_layout: Option, +} + +impl From for CompilationArtifacts { + fn from(value: T) -> Self { + Self { + abi: value.abi(), + devdoc: value.devdoc(), + userdoc: value.userdoc(), + storage_layout: value.storage_layout(), + } + } +} + +impl From for Value { + fn from(value: CompilationArtifacts) -> Self { + serde_json::to_value(value).expect("compilation artifacts serialization must succeed") + } +} diff --git a/libs/verification-common/src/verifier_alliance/creation_code_artifacts.rs b/libs/verification-common/src/verifier_alliance/creation_code_artifacts.rs new file mode 100644 index 000000000..1520aff12 --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/creation_code_artifacts.rs @@ -0,0 +1,53 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub trait ToCreationCodeArtifacts { + fn cbor_auxdata(&self) -> Option { + None + } + fn link_references(&self) -> Option { + None + } + fn source_map(&self) -> Option { + None + } +} + +impl ToCreationCodeArtifacts for &T { + fn cbor_auxdata(&self) -> Option { + (*self).cbor_auxdata() + } + fn link_references(&self) -> Option { + (*self).link_references() + } + fn source_map(&self) -> Option { + (*self).source_map() + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreationCodeArtifacts { + #[serde(skip_serializing_if = "Option::is_none")] + pub source_map: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub link_references: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cbor_auxdata: Option, +} + +impl From for CreationCodeArtifacts { + fn from(value: T) -> Self { + Self { + link_references: value.link_references(), + source_map: value.source_map(), + cbor_auxdata: value.cbor_auxdata(), + } + } +} + +impl From for Value { + fn from(value: CreationCodeArtifacts) -> Self { + serde_json::to_value(value).expect("creation code artifacts serialization must succeed") + } +} diff --git a/libs/verification-common/src/verifier_alliance/mod.rs b/libs/verification-common/src/verifier_alliance/mod.rs new file mode 100644 index 000000000..adb38f75b --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/mod.rs @@ -0,0 +1,12 @@ +mod compilation_artifacts; +mod creation_code_artifacts; +mod runtime_code_artifacts; +mod verification_match; + +mod verification_match_transformations; +mod verification_match_values; + +pub use compilation_artifacts::{CompilationArtifacts, ToCompilationArtifacts}; +pub use creation_code_artifacts::{CreationCodeArtifacts, ToCreationCodeArtifacts}; +pub use runtime_code_artifacts::{RuntimeCodeArtifacts, ToRuntimeCodeArtifacts}; +pub use verification_match::{Match, MatchBuilder, MatchTransformation, MatchType, MatchValues}; diff --git a/libs/verification-common/src/verifier_alliance/runtime_code_artifacts.rs b/libs/verification-common/src/verifier_alliance/runtime_code_artifacts.rs new file mode 100644 index 000000000..034a2b84f --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/runtime_code_artifacts.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub trait ToRuntimeCodeArtifacts { + fn cbor_auxdata(&self) -> Option { + None + } + fn immutable_references(&self) -> Option { + None + } + fn link_references(&self) -> Option { + None + } + fn source_map(&self) -> Option { + None + } +} + +impl ToRuntimeCodeArtifacts for &T { + fn cbor_auxdata(&self) -> Option { + (*self).cbor_auxdata() + } + fn immutable_references(&self) -> Option { + (*self).immutable_references() + } + fn link_references(&self) -> Option { + (*self).link_references() + } + fn source_map(&self) -> Option { + (*self).source_map() + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RuntimeCodeArtifacts { + #[serde(skip_serializing_if = "Option::is_none")] + pub cbor_auxdata: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub immutable_references: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub link_references: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub source_map: Option, +} + +impl From for RuntimeCodeArtifacts { + fn from(value: T) -> Self { + Self { + cbor_auxdata: value.cbor_auxdata(), + immutable_references: value.immutable_references(), + link_references: value.link_references(), + source_map: value.source_map(), + } + } +} + +impl From<(RuntimeCodeArtifacts, RuntimeCodeArtifacts)> for RuntimeCodeArtifacts { + fn from( + (base_artifacts, merged_artifacts): (RuntimeCodeArtifacts, RuntimeCodeArtifacts), + ) -> Self { + Self { + cbor_auxdata: merged_artifacts + .cbor_auxdata + .or(base_artifacts.cbor_auxdata), + immutable_references: merged_artifacts + .immutable_references + .or(base_artifacts.immutable_references), + link_references: merged_artifacts + .link_references + .or(base_artifacts.link_references), + source_map: merged_artifacts.source_map.or(base_artifacts.source_map), + } + } +} + +impl From for Value { + fn from(value: RuntimeCodeArtifacts) -> Self { + serde_json::to_value(value).expect("runtime code artifacts serialization must succeed") + } +} diff --git a/libs/verification-common/src/verifier_alliance/verification_match.rs b/libs/verification-common/src/verifier_alliance/verification_match.rs new file mode 100644 index 000000000..73c125e32 --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/verification_match.rs @@ -0,0 +1,171 @@ +use super::{ + compilation_artifacts::CompilationArtifacts, creation_code_artifacts::CreationCodeArtifacts, + runtime_code_artifacts::RuntimeCodeArtifacts, +}; +pub use super::{ + verification_match_transformations::Transformation as MatchTransformation, + verification_match_values::Values as MatchValues, +}; +use alloy_dyn_abi::JsonAbiExt; +use anyhow::Context; +use bytes::Bytes; +use serde::Deserialize; +use std::fmt::{Display, Formatter}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MatchType { + Full, + Partial, +} + +impl Display for MatchType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + MatchType::Full => f.write_str("full"), + MatchType::Partial => f.write_str("partial"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Match { + pub r#type: MatchType, + pub transformations: Vec, + pub values: MatchValues, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MatchBuilder<'a> { + deployed_code: &'a [u8], + compiled_code: Vec, + transformations: Vec, + values: MatchValues, + invalid_constructor_arguments: bool, + has_cbor_auxdata: bool, + has_cbor_auxdata_transformation: bool, +} + +impl<'a> MatchBuilder<'a> { + pub fn new(deployed_code: &'a [u8], compiled_code: Vec) -> Option { + if deployed_code.len() < compiled_code.len() { + return None; + } + + Some(Self { + deployed_code, + compiled_code, + transformations: vec![], + values: MatchValues::default(), + invalid_constructor_arguments: false, + has_cbor_auxdata: false, + has_cbor_auxdata_transformation: false, + }) + } + + pub fn set_has_cbor_auxdata(mut self, value: bool) -> Self { + self.has_cbor_auxdata = value; + self + } + + pub fn apply_runtime_code_transformations( + self, + runtime_code_artifacts: &RuntimeCodeArtifacts, + ) -> Result { + self.apply_cbor_auxdata_transformations(runtime_code_artifacts.cbor_auxdata.as_ref())? + .apply_library_transformations(runtime_code_artifacts.link_references.as_ref())? + .apply_immutable_transformations(runtime_code_artifacts.immutable_references.as_ref()) + } + + pub fn apply_creation_code_transformations( + self, + creation_code_artifacts: &CreationCodeArtifacts, + compilation_artifacts: &CompilationArtifacts, + ) -> Result { + self.apply_cbor_auxdata_transformations(creation_code_artifacts.cbor_auxdata.as_ref())? + .apply_library_transformations(creation_code_artifacts.link_references.as_ref())? + .apply_constructor_transformation(compilation_artifacts.abi.as_ref()) + } + + pub fn verify_and_build(self) -> Option { + if !self.invalid_constructor_arguments + && self.deployed_code == self.compiled_code.as_slice() + { + let match_type = if self.has_cbor_auxdata_transformation || !self.has_cbor_auxdata { + MatchType::Partial + } else { + MatchType::Full + }; + return Some(Match { + r#type: match_type, + transformations: self.transformations, + values: self.values, + }); + } + + None + } + + fn apply_cbor_auxdata_transformations( + self, + _cbor_auxdata: Option<&serde_json::Value>, + ) -> Result { + Ok(self) + } + + fn apply_library_transformations( + self, + _link_references: Option<&serde_json::Value>, + ) -> Result { + Ok(self) + } + + fn apply_immutable_transformations( + self, + _immutable_references: Option<&serde_json::Value>, + ) -> Result { + Ok(self) + } + + fn apply_constructor_transformation( + mut self, + abi: Option<&serde_json::Value>, + ) -> Result { + let offset = self.compiled_code.len(); + let (_prefix, constructor_arguments) = self.deployed_code.split_at(offset); + + let constructor = match abi { + Some(abi) => { + alloy_json_abi::JsonAbi::deserialize(abi) + .context("parsing compiled contract abi")? + .constructor + } + None => None, + }; + + match constructor { + None if !constructor_arguments.is_empty() => { + self.invalid_constructor_arguments = true; + } + Some(_constructor) if constructor_arguments.is_empty() => { + self.invalid_constructor_arguments = true; + } + Some(constructor) + if constructor + .abi_decode_input(constructor_arguments, true) + .is_err() => + { + self.invalid_constructor_arguments = true; + } + None => {} + Some(_constructor) => { + self.compiled_code.extend(constructor_arguments); + self.transformations + .push(MatchTransformation::constructor(offset)); + self.values + .add_constructor_arguments(Bytes::copy_from_slice(constructor_arguments)); + } + } + + Ok(self) + } +} diff --git a/libs/verification-common/src/verifier_alliance/verification_match_transformations.rs b/libs/verification-common/src/verifier_alliance/verification_match_transformations.rs new file mode 100644 index 000000000..006509870 --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/verification_match_transformations.rs @@ -0,0 +1,71 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +enum TransformationType { + Insert, + Replace, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +enum TransformationReason { + Auxdata, + Constructor, + Immutable, + Library, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Transformation { + r#type: TransformationType, + reason: TransformationReason, + offset: usize, + #[serde(skip_serializing_if = "Option::is_none")] + id: Option, +} + +impl From for serde_json::Value { + fn from(value: Transformation) -> Self { + serde_json::to_value(value).expect("transformations serialization must succeed") + } +} + +impl Transformation { + pub fn auxdata(offset: usize, id: String) -> Self { + Self { + r#type: TransformationType::Replace, + reason: TransformationReason::Auxdata, + offset, + id: Some(id), + } + } + + pub fn constructor(offset: usize) -> Self { + Self { + r#type: TransformationType::Insert, + reason: TransformationReason::Constructor, + offset, + id: None, + } + } + + pub fn immutable(offset: usize, id: String) -> Self { + Self { + r#type: TransformationType::Replace, + reason: TransformationReason::Immutable, + offset, + id: Some(id), + } + } + + pub fn library(offset: usize, id: String) -> Self { + Self { + r#type: TransformationType::Replace, + reason: TransformationReason::Library, + offset, + id: Some(id), + } + } +} diff --git a/libs/verification-common/src/verifier_alliance/verification_match_values.rs b/libs/verification-common/src/verifier_alliance/verification_match_values.rs new file mode 100644 index 000000000..1ddab3df2 --- /dev/null +++ b/libs/verification-common/src/verifier_alliance/verification_match_values.rs @@ -0,0 +1,46 @@ +use bytes::Bytes; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use std::collections::BTreeMap; + +#[serde_as] +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Values { + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + #[serde_as(as = "BTreeMap<_, blockscout_display_bytes::serde_as::Hex>")] + cbor_auxdata: BTreeMap, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option")] + constructor_arguments: Option, + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + #[serde_as(as = "BTreeMap<_, blockscout_display_bytes::serde_as::Hex>")] + libraries: BTreeMap, + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + #[serde_as(as = "BTreeMap<_, blockscout_display_bytes::serde_as::Hex>")] + immutables: BTreeMap, +} + +impl From for serde_json::Value { + fn from(value: Values) -> Self { + serde_json::to_value(value).expect("values serialization must succeed") + } +} + +impl Values { + pub fn add_cbor_auxdata(&mut self, key: impl Into, value: Bytes) { + self.cbor_auxdata.insert(key.into(), value); + } + + pub fn add_constructor_arguments(&mut self, value: Bytes) { + self.constructor_arguments = Some(value); + } + + pub fn add_library(&mut self, key: impl Into, value: Bytes) { + self.libraries.insert(key.into(), value); + } + + pub fn add_immutable(&mut self, key: impl Into, value: Bytes) { + self.immutables.insert(key.into(), value); + } +} diff --git a/smart-contract-verifier/Cargo.lock b/smart-contract-verifier/Cargo.lock index 8473468a4..05c137479 100644 --- a/smart-contract-verifier/Cargo.lock +++ b/smart-contract-verifier/Cargo.lock @@ -83,6 +83,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "actix-prost" +version = "0.1.0" +source = "git+https://github.com/blockscout/actix-prost?rev=9cc47aa1#9cc47aa1cb7b63ce1cb814912b71188579647241" +dependencies = [ + "actix-http", + "actix-web", + "http", + "prost 0.11.9", + "serde", + "serde_json", + "serde_with 2.3.3", + "tonic", +] + [[package]] name = "actix-prost" version = "0.1.0" @@ -91,19 +106,35 @@ dependencies = [ "actix-http", "actix-web", "http", - "prost", + "prost 0.11.9", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "tonic", ] +[[package]] +name = "actix-prost-build" +version = "0.1.0" +source = "git+https://github.com/blockscout/actix-prost?rev=9cc47aa1#9cc47aa1cb7b63ce1cb814912b71188579647241" +dependencies = [ + "prettyplease 0.2.20", + "proc-macro2", + "prost-build", + "prost-reflect", + "quote", + "serde", + "serde_yaml", + "syn 2.0.66", + "thiserror", +] + [[package]] name = "actix-prost-build" version = "0.1.0" source = "git+https://github.com/blockscout/actix-prost#a060c2fe886d4551ed7350fa445d3d2bc02758f4" dependencies = [ - "prettyplease", + "prettyplease 0.1.25", "proc-macro2", "prost-build", "quote", @@ -113,6 +144,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "actix-prost-macros" +version = "0.1.0" +source = "git+https://github.com/blockscout/actix-prost?rev=9cc47aa1#9cc47aa1cb7b63ce1cb814912b71188579647241" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "actix-prost-macros" version = "0.1.0" @@ -328,10 +369,27 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2919acdad13336bc5dc26b636cdd6892c2f27fb0d4a58320a00c2713cf6a4e9a" dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", + "alloy-json-abi 0.6.4", + "alloy-primitives 0.6.4", + "alloy-sol-type-parser 0.6.4", + "alloy-sol-types 0.6.4", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.5", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6e6436a9530f25010d13653e206fab4c9feddacf21a54de8d7311b275bc56b" +dependencies = [ + "alloy-json-abi 0.7.6", + "alloy-primitives 0.7.6", + "alloy-sol-type-parser 0.7.6", + "alloy-sol-types 0.7.6", "const-hex", "itoa", "serde", @@ -345,8 +403,20 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24ed0f2a6c3a1c947b4508522a53a190dba8f94dcd4e3e1a5af945a498e78f2f" dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", + "alloy-primitives 0.6.4", + "alloy-sol-type-parser 0.6.4", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-abi" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeaccd50238126e3a0ff9387c7c568837726ad4f4e399b528ca88104d6c25ef" +dependencies = [ + "alloy-primitives 0.7.6", + "alloy-sol-type-parser 0.7.6", "serde", "serde_json", ] @@ -373,6 +443,28 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-primitives" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand 0.8.5", + "ruint", + "serde", + "tiny-keccak", +] + [[package]] name = "alloy-rlp" version = "0.3.4" @@ -391,16 +483,63 @@ checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" dependencies = [ "const-hex", "dunce", - "heck", + "heck 0.4.1", + "indexmap 2.2.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", + "syn-solidity 0.6.4", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", "indexmap 2.2.5", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.42", - "syn-solidity", + "syn 2.0.66", + "syn-solidity 0.7.6", "tiny-keccak", ] +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", + "syn-solidity 0.7.6", +] + [[package]] name = "alloy-sol-type-parser" version = "0.6.4" @@ -410,34 +549,56 @@ dependencies = [ "winnow 0.6.5", ] +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" +dependencies = [ + "winnow 0.6.5", +] + [[package]] name = "alloy-sol-types" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" dependencies = [ - "alloy-primitives", - "alloy-sol-macro", + "alloy-primitives 0.6.4", + "alloy-sol-macro 0.6.4", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" +dependencies = [ + "alloy-primitives 0.7.6", + "alloy-sol-macro 0.7.6", "const-hex", "serde", ] [[package]] name = "amplify" -version = "3.14.2" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ec14f4fb838e9ddace42fa5944bb1ee4dff8477494ba48c5f874e16caf27a" +checksum = "9e711289a6cb28171b4f0e6c8019c69ff9476050508dc082167575d458ff74d0" dependencies = [ "amplify_derive", "amplify_num", + "ascii", "wasm-bindgen", ] [[package]] name = "amplify_derive" -version = "2.11.3" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c3de270e75f27a4468a7c344070109046656e85cb522141f7d40ab4b83803ac" +checksum = "759dcbfaf94d838367a86d493ec34ccc8aa6fe365cb7880d6bf89006de24d9c1" dependencies = [ "amplify_syn", "proc-macro2", @@ -447,15 +608,18 @@ dependencies = [ [[package]] name = "amplify_num" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27d3d00d3d115395a7a8a4dc045feb7aa82b641e485f7e15f4e67ac16f4f56d" +checksum = "04c009c5c4de814911b177e2ea59e4930bb918978ed3cce4900d846a6ceb0838" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "amplify_syn" -version = "1.1.6" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da24db1445cc7bc3842fa072c2d51fe5b25b812b6a572d65842a4c72e87221ac" +checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" dependencies = [ "proc-macro2", "quote", @@ -619,6 +783,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -668,7 +838,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -679,7 +849,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -819,6 +989,12 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -875,21 +1051,23 @@ dependencies = [ [[package]] name = "blockscout-display-bytes" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4acc2bb1d1de55a55f818c10cdbc4a698f778666a7b17d2eab737aa6ed1b5e" +checksum = "34a6e9076f2cb26f696b58c0dfed47cf4b7499b4328b1fa797b3a34d747712e5" dependencies = [ "bytes", "hex", "serde", + "serde_json", + "serde_with 3.8.1", "thiserror", ] [[package]] name = "blockscout-service-launcher" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545355abddfc1617d40529141b7e849feed732328ccf54850f691dc31cf24856" +checksum = "a78f3c680d4ae2ad571097fc87d9cacf27f29bf0a4ecb5df10bd3845b62e8b65" dependencies = [ "actix-cors", "actix-web", @@ -899,8 +1077,8 @@ dependencies = [ "config", "futures", "once_cell", - "opentelemetry 0.19.0", - "opentelemetry-jaeger 0.18.0", + "opentelemetry", + "opentelemetry-jaeger", "prometheus", "reqwest", "serde", @@ -908,7 +1086,7 @@ dependencies = [ "tokio", "tonic", "tracing", - "tracing-opentelemetry 0.19.0", + "tracing-opentelemetry", "tracing-subscriber", "uuid", ] @@ -955,9 +1133,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -1168,9 +1346,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1222,12 +1400,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1288,7 +1463,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -1299,7 +1474,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -1545,24 +1720,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +name = "era-compiler-common" +version = "1.5.0" +source = "git+https://github.com/matter-labs/era-compiler-common?rev=9969de1#9969de17367fc850cd2c7fc82dc26a4ade550a05" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", + "anyhow", + "serde", + "serde_json", + "serde_stacker", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -1737,9 +1912,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -1790,11 +1965,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1b77c95e79bff02ddaa38426fc6809a3a438dce0e6a2eb212dac97da7c157b4" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.6.4", + "alloy-primitives 0.6.4", "cfg-if", "dirs 5.0.1", "dunce", + "futures-util", "home", "itertools 0.12.1", "md-5", @@ -1809,6 +1985,7 @@ dependencies = [ "solang-parser", "svm-rs", "thiserror", + "tokio", "tracing", "walkdir", "yansi", @@ -1907,7 +2084,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -2044,6 +2221,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.1" @@ -2296,6 +2479,7 @@ checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -2512,9 +2696,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "local-channel" @@ -2546,9 +2730,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -2593,9 +2777,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -2633,22 +2817,22 @@ dependencies = [ [[package]] name = "minicbor" -version = "0.18.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a20020e8e2d1881d8736f64011bb5ff99f1db9947ce3089706945c8915695cb" +checksum = "5f8e213c36148d828083ae01948eed271d03f95f7e72571fa242d78184029af2" dependencies = [ "minicbor-derive", ] [[package]] name = "minicbor-derive" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8608fb1c805b5b6b3d5ab7bd95c40c396df622b64d77b2d621a5eae1eed050ee" +checksum = "a6bdc119b1a405df86a8cde673295114179dbd0ebe18877c26ba89fb080365c2" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -2747,7 +2931,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -2796,6 +2980,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303e8749c804ccd6ca3b428de7fe0d86cb86bc7606bc15291f100fd487960bb8" + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -2870,7 +3060,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -2936,7 +3126,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -2957,41 +3147,14 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e" -dependencies = [ - "opentelemetry_api 0.18.0", - "opentelemetry_sdk 0.18.0", -] - [[package]] name = "opentelemetry" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f" dependencies = [ - "opentelemetry_api 0.19.0", - "opentelemetry_sdk 0.19.0", -] - -[[package]] -name = "opentelemetry-jaeger" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e785d273968748578931e4dc3b4f5ec86b26e09d9e0d66b55adda7fce742f7a" -dependencies = [ - "async-trait", - "futures", - "futures-executor", - "once_cell", - "opentelemetry 0.18.0", - "opentelemetry-semantic-conventions 0.10.0", - "thiserror", - "thrift 0.16.0", - "tokio", + "opentelemetry_api", + "opentelemetry_sdk", ] [[package]] @@ -3004,45 +3167,20 @@ dependencies = [ "futures", "futures-executor", "once_cell", - "opentelemetry 0.19.0", - "opentelemetry-semantic-conventions 0.11.0", + "opentelemetry", + "opentelemetry-semantic-conventions", "thiserror", - "thrift 0.17.0", + "thrift", "tokio", ] -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b02e0230abb0ab6636d18e2ba8fa02903ea63772281340ccac18e0af3ec9eeb" -dependencies = [ - "opentelemetry 0.18.0", -] - [[package]] name = "opentelemetry-semantic-conventions" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24e33428e6bf08c6f7fcea4ddb8e358fab0fe48ab877a87c70c6ebe20f673ce5" dependencies = [ - "opentelemetry 0.19.0", -] - -[[package]] -name = "opentelemetry_api" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22" -dependencies = [ - "fnv", - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", + "opentelemetry", ] [[package]] @@ -3061,28 +3199,6 @@ dependencies = [ "urlencoding", ] -[[package]] -name = "opentelemetry_sdk" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113" -dependencies = [ - "async-trait", - "crossbeam-channel", - "dashmap", - "fnv", - "futures-channel", - "futures-executor", - "futures-util", - "once_cell", - "opentelemetry_api 0.18.0", - "percent-encoding", - "rand 0.8.5", - "thiserror", - "tokio", - "tokio-stream", -] - [[package]] name = "opentelemetry_sdk" version = "0.19.0" @@ -3097,7 +3213,7 @@ dependencies = [ "futures-executor", "futures-util", "once_cell", - "opentelemetry_api 0.19.0", + "opentelemetry_api", "percent-encoding", "rand 0.8.5", "thiserror", @@ -3111,15 +3227,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-float" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-float" version = "2.10.1" @@ -3216,7 +3323,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", "winapi", ] @@ -3229,7 +3336,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", "windows-sys 0.45.0", ] @@ -3311,7 +3418,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -3403,7 +3510,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -3509,6 +3616,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.66", +] + [[package]] name = "primitive-types" version = "0.12.1" @@ -3559,9 +3676,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -3608,7 +3725,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", ] [[package]] @@ -3618,15 +3745,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", - "heck", + "heck 0.4.1", "itertools 0.10.5", "lazy_static", "log", "multimap", "petgraph", - "prettyplease", - "prost", - "prost-types", + "prettyplease 0.1.25", + "prost 0.11.9", + "prost-types 0.11.9", "regex", "syn 1.0.109", "tempfile", @@ -3646,13 +3773,46 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "prost-reflect" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" +dependencies = [ + "once_cell", + "prost 0.12.6", + "prost-types 0.12.6", +] + [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", ] [[package]] @@ -3661,6 +3821,15 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3679,9 +3848,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -3801,15 +3970,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_users" version = "0.4.3" @@ -3817,7 +3977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.9", - "redox_syscall 0.2.16", + "redox_syscall", "thiserror", ] @@ -4111,13 +4271,13 @@ dependencies = [ [[package]] name = "rstest" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" dependencies = [ "futures", "futures-timer", - "rstest_macros 0.18.2", + "rstest_macros 0.19.0", "rustc_version 0.4.0", ] @@ -4136,9 +4296,9 @@ dependencies = [ [[package]] name = "rstest_macros" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" dependencies = [ "cfg-if", "glob", @@ -4147,15 +4307,15 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.0", - "syn 2.0.42", + "syn 2.0.66", "unicode-ident", ] [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4177,9 +4337,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rust-ini" @@ -4268,15 +4428,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.10", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", ] [[package]] @@ -4475,9 +4635,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.164" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -4496,13 +4656,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -4527,6 +4687,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_stacker" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "babfccff5773ff80657f0ecf553c7c516bdc2eb16389c0918b36b73e7015276e" +dependencies = [ + "serde", + "stacker", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4551,7 +4721,25 @@ dependencies = [ "indexmap 1.9.3", "serde", "serde_json", - "serde_with_macros", + "serde_with_macros 2.3.3", + "time 0.3.21", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.5", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.8.1", "time 0.3.21", ] @@ -4564,7 +4752,19 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", ] [[package]] @@ -4639,7 +4839,7 @@ dependencies = [ "http", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "sig-provider-proto", "smart-contract-verifier", "tokio", @@ -4651,15 +4851,15 @@ name = "sig-provider-proto" version = "0.1.0" source = "git+https://github.com/blockscout/blockscout-rs#572d62a37133fe87da00baaf56128496453d09e8" dependencies = [ - "actix-prost", - "actix-prost-build", - "actix-prost-macros", + "actix-prost 0.1.0 (git+https://github.com/blockscout/actix-prost)", + "actix-prost-build 0.1.0 (git+https://github.com/blockscout/actix-prost)", + "actix-prost-macros 0.1.0 (git+https://github.com/blockscout/actix-prost)", "actix-web", "async-trait", - "prost", + "prost 0.11.9", "prost-build", "serde", - "serde_with", + "serde_with 2.3.3", "tonic", "tonic-build", ] @@ -4700,16 +4900,16 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smart-contract-verifier" version = "0.6.0" dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", + "alloy-dyn-abi 0.6.4", + "alloy-json-abi 0.6.4", "anyhow", "async-trait", "blockscout-display-bytes", @@ -4717,6 +4917,7 @@ dependencies = [ "chrono", "const_format", "cron", + "era-compiler-common", "ethabi", "ethers-core", "ethers-solc", @@ -4724,9 +4925,9 @@ dependencies = [ "futures", "hex", "lazy_static", - "minicbor", "mismatch", "mockall 0.11.4", + "nonempty", "parking_lot 0.12.1", "pretty_assertions", "primitive-types", @@ -4741,7 +4942,7 @@ dependencies = [ "semver 1.0.17", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "sha2", "solidity-metadata", "sourcify", @@ -4759,20 +4960,20 @@ dependencies = [ name = "smart-contract-verifier-proto" version = "0.1.0" dependencies = [ - "actix-prost", - "actix-prost-build", - "actix-prost-macros", + "actix-prost 0.1.0 (git+https://github.com/blockscout/actix-prost?rev=9cc47aa1)", + "actix-prost-build 0.1.0 (git+https://github.com/blockscout/actix-prost?rev=9cc47aa1)", + "actix-prost-macros 0.1.0 (git+https://github.com/blockscout/actix-prost?rev=9cc47aa1)", "actix-web", "anyhow", "async-trait", "mockall 0.12.1", - "prost", + "prost 0.11.9", "prost-build", "reqwest", "reqwest-middleware 0.2.4", "reqwest-retry 0.3.0", "serde", - "serde_with", + "serde_with 2.3.3", "smart-contract-verifier-proto", "thiserror", "tokio", @@ -4802,28 +5003,26 @@ dependencies = [ "futures", "hex", "lazy_static", - "opentelemetry 0.18.0", - "opentelemetry-jaeger 0.17.0", "pretty_assertions", "prometheus", "reqwest", - "rstest 0.18.2", + "rstest 0.19.0", "rust-s3", "serde", "serde_json", - "serde_with", + "serde_with 3.8.1", "sig-provider-extension", "smart-contract-verifier", "smart-contract-verifier-proto", "stdext", + "tempfile", "thiserror", "tokio", "tonic", "tracing", - "tracing-opentelemetry 0.18.0", - "tracing-subscriber", "url", "uuid", + "verification-common", ] [[package]] @@ -4873,11 +5072,10 @@ dependencies = [ [[package]] name = "solidity-metadata" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f0ee752c2f80c87498dba10c2120492c42073f73b37ffb49e79fcd1e91bff3" +checksum = "27866969b7fa575837afa26079574f78f1e8223ac2f933e5808060b204ba23d8" dependencies = [ - "blockscout-display-bytes", "minicbor", "semver 1.0.17", "thiserror", @@ -4886,7 +5084,7 @@ dependencies = [ [[package]] name = "sourcify" version = "0.1.0" -source = "git+https://github.com/blockscout/blockscout-rs?rev=457af68#457af68c8c09046530ec174112be6443ae34ff19" +source = "git+https://github.com/blockscout/blockscout-rs?rev=c4261d0#c4261d0f7bec3c250a979b416ed48ab13853c9d6" dependencies = [ "anyhow", "blockscout-display-bytes", @@ -4947,6 +5145,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4993,11 +5204,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -5052,9 +5263,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -5070,7 +5281,19 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", +] + +[[package]] +name = "syn-solidity" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.66", ] [[package]] @@ -5117,15 +5340,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.4.1", - "rustix 0.38.21", - "windows-sys 0.48.0", + "rustix 0.38.34", + "windows-sys 0.52.0", ] [[package]] @@ -5147,22 +5369,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -5184,19 +5406,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "thrift" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09678c4cdbb4eed72e18b7c2af1329c69825ed16fcbac62d083fc3e2b0590ff0" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float 1.1.1", - "threadpool", -] - [[package]] name = "thrift" version = "0.17.0" @@ -5206,7 +5415,7 @@ dependencies = [ "byteorder", "integer-encoding", "log", - "ordered-float 2.10.1", + "ordered-float", "threadpool", ] @@ -5309,7 +5518,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -5403,8 +5612,8 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", - "prost-derive", + "prost 0.11.9", + "prost-derive 0.11.9", "tokio", "tokio-stream", "tokio-util", @@ -5421,7 +5630,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ - "prettyplease", + "prettyplease 0.1.25", "proc-macro2", "prost-build", "quote", @@ -5481,7 +5690,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] @@ -5515,20 +5724,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de" -dependencies = [ - "once_cell", - "opentelemetry 0.18.0", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - [[package]] name = "tracing-opentelemetry" version = "0.19.0" @@ -5536,7 +5731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00a39dcf9bfc1742fa4d6215253b33a6e474be78275884c216fc2a06267b3600" dependencies = [ "once_cell", - "opentelemetry 0.19.0", + "opentelemetry", "tracing", "tracing-core", "tracing-log", @@ -5706,9 +5901,16 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "verification-common" version = "0.1.0" -source = "git+https://github.com/blockscout/blockscout-rs?rev=7a6e940#7a6e9400e26772a7e387d6aebb807eccf166872b" +source = "git+https://github.com/blockscout/blockscout-rs/?branch=rimrakhimov/verifier/zksolc-support#ebcd9e62fdbe9cb1b2f8fcc3a73ca0ec6d2b3eeb" dependencies = [ + "alloy-dyn-abi 0.7.6", + "alloy-json-abi 0.7.6", + "anyhow", + "blockscout-display-bytes", "bytes", + "serde", + "serde_json", + "serde_with 3.8.1", ] [[package]] @@ -5791,7 +5993,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -5825,7 +6027,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5970,6 +6172,15 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -6000,6 +6211,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6012,6 +6239,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6024,6 +6257,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6036,6 +6275,18 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6048,6 +6299,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6060,6 +6317,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6072,6 +6335,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6084,6 +6353,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" version = "0.4.6" @@ -6181,7 +6456,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.42", + "syn 2.0.66", ] [[package]] diff --git a/smart-contract-verifier/Cargo.toml b/smart-contract-verifier/Cargo.toml index d3ae7eacd..50d1d5a1d 100644 --- a/smart-contract-verifier/Cargo.toml +++ b/smart-contract-verifier/Cargo.toml @@ -8,4 +8,6 @@ members = [ ] [workspace.dependencies] -blockscout-service-launcher = "0.10.0" \ No newline at end of file +blockscout-display-bytes = "1.1.0" +blockscout-service-launcher = "0.12.0" +verification-common = { git = "https://github.com/blockscout/blockscout-rs/", branch = "rimrakhimov/verifier/zksolc-support" } \ No newline at end of file diff --git a/smart-contract-verifier/smart-contract-verifier-proto/Cargo.toml b/smart-contract-verifier/smart-contract-verifier-proto/Cargo.toml index 3d6928baa..5a5818118 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/Cargo.toml +++ b/smart-contract-verifier/smart-contract-verifier-proto/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-prost = { git = "https://github.com/blockscout/actix-prost" } -actix-prost-macros = { git = "https://github.com/blockscout/actix-prost" } +actix-prost = { git = "https://github.com/blockscout/actix-prost", rev = "9cc47aa1" } +actix-prost-macros = { git = "https://github.com/blockscout/actix-prost", rev = "9cc47aa1" } actix-web = "4" async-trait = "0.1" prost = "0.11" @@ -25,7 +25,7 @@ mockall = { version = "0.12.1", optional = true } tokio = { version = "1", optional = true } [build-dependencies] -actix-prost-build = { git = "https://github.com/blockscout/actix-prost" } +actix-prost-build = { git = "https://github.com/blockscout/actix-prost", rev = "9cc47aa1" } tonic-build = "0.8" prost-build = "0.11" diff --git a/smart-contract-verifier/smart-contract-verifier-proto/build.rs b/smart-contract-verifier/smart-contract-verifier-proto/build.rs index cf228f12a..831f09f7a 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/build.rs +++ b/smart-contract-verifier/smart-contract-verifier-proto/build.rs @@ -51,6 +51,7 @@ fn main() -> Result<(), Box> { compile( &[ "proto/v2/smart-contract-verifier.proto", + "proto/v2/zksync-solidity.proto", "proto/v2/health.proto", ], &["proto"], diff --git a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml index 901d93ca8..234bfb997 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml +++ b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/api_config_http.yaml @@ -52,6 +52,15 @@ http: post: /api/v2/verifier/sourcify/sources:verify-from-etherscan body: "*" + #################### ZkSync Solidity Verifier #################### + + - selector: blockscout.smartContractVerifier.v2.zksync.solidity.ZkSyncSolidityVerifier.VerifyStandardJson + post: /api/v2/zksync-verifier/solidity/sources:verify-standard-json + body: "*" + + - selector: blockscout.smartContractVerifier.v2.zksync.solidity.ZkSyncSolidityVerifier.ListCompilers + get: /api/v2/zksync-verifier/solidity/versions + #################### Health #################### - selector: blockscout.smartContractVerifier.v2.Health.Check diff --git a/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/zksync-solidity.proto b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/zksync-solidity.proto new file mode 100644 index 000000000..c38932ab5 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-proto/proto/v2/zksync-solidity.proto @@ -0,0 +1,71 @@ +syntax = "proto3"; + +package blockscout.smartContractVerifier.v2.zksync.solidity; + +option go_package = "github.com/blockscout/blockscout-rs/smart-contract-verifier/zksync/solidity"; + +service ZkSyncSolidityVerifier { + rpc VerifyStandardJson(VerifyStandardJsonRequest) returns (VerifyResponse) {} + + rpc ListCompilers(ListCompilersRequest) returns (ListCompilersResponse) {} +} + +message VerifyStandardJsonRequest { + string code = 1; + optional string constructor_arguments = 2; + string zk_compiler = 3; + string solc_compiler = 4; + string input = 5; +} + +message CompilationFailure { + string message = 1; +} + +message VerificationFailure { + string message = 1; +} + +message VerificationSuccess { + string file_name = 1; + string contract_name = 2; + string zk_compiler = 3; + string zk_compiler_version = 4; + string evm_compiler = 5; + string evm_compiler_version = 6; + string language = 7; + string compiler_settings = 8; + map sources = 9; + string compilation_artifacts = 10; + string creation_code_artifacts = 11; + string runtime_code_artifacts = 12; + + optional Match creation_match = 13; + Match runtime_match = 14; +} + +message VerifyResponse { + oneof verify_response { + CompilationFailure compilation_failure = 1; + VerificationFailure verification_failure = 2; + VerificationSuccess verification_success = 3; + } +} + +message ListCompilersRequest {} + +message ListCompilersResponse { + repeated string zk_compilers = 1; + repeated string solc_compilers = 2; +} + +message Match { + enum MatchType { + MATCH_TYPE_UNSPECIFIED = 0; + PARTIAL = 1; + FULL = 2; + } + MatchType type = 1; + string values = 2; + string transformations = 3; +} diff --git a/smart-contract-verifier/smart-contract-verifier-proto/src/lib.rs b/smart-contract-verifier/smart-contract-verifier-proto/src/lib.rs index 7be91ba16..43cb73082 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/src/lib.rs +++ b/smart-contract-verifier/smart-contract-verifier-proto/src/lib.rs @@ -10,6 +10,15 @@ pub mod blockscout { env!("OUT_DIR"), "/blockscout.smart_contract_verifier.v2.rs" )); + + pub mod zksync { + pub mod solidity { + include!(concat!( + env!("OUT_DIR"), + "/blockscout.smart_contract_verifier.v2.zksync.solidity.rs" + )); + } + } } } } diff --git a/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml b/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml index e5aefb4f9..7aa8d29b7 100644 --- a/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml +++ b/smart-contract-verifier/smart-contract-verifier-proto/swagger/v2/smart-contract-verifier.swagger.yaml @@ -6,6 +6,7 @@ tags: - name: SolidityVerifier - name: VyperVerifier - name: SourcifyVerifier + - name: ZkSyncSolidityVerifier - name: Health consumes: - application/json @@ -79,7 +80,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -99,7 +100,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -133,7 +134,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -153,7 +154,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -173,7 +174,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -193,7 +194,7 @@ paths: "200": description: A successful response. schema: - $ref: '#/definitions/v2VerifyResponse' + $ref: '#/definitions/smartContractVerifierv2VerifyResponse' default: description: An unexpected error response. schema: @@ -220,6 +221,40 @@ paths: $ref: '#/definitions/googlerpcStatus' tags: - VyperVerifier + /api/v2/zksync-verifier/solidity/sources:verify-standard-json: + post: + operationId: ZkSyncSolidityVerifier_VerifyStandardJson + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/zksyncsolidityVerifyResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/solidityVerifyStandardJsonRequest' + tags: + - ZkSyncSolidityVerifier + /api/v2/zksync-verifier/solidity/versions: + get: + operationId: ZkSyncSolidityVerifier_ListCompilers + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/solidityListCompilersResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + tags: + - ZkSyncSolidityVerifier /health: get: summary: |- @@ -357,11 +392,107 @@ definitions: '@type': type: string additionalProperties: {} + smartContractVerifierv2CompilationFailure: + type: object + properties: + message: + type: string + smartContractVerifierv2VerifyResponse: + type: object + properties: + message: + type: string + status: + $ref: '#/definitions/v2VerifyResponseStatus' + source: + $ref: '#/definitions/v2Source' + extraData: + $ref: '#/definitions/VerifyResponseExtraData' + postActionResponses: + $ref: '#/definitions/VerifyResponsePostActionResponses' + solidityListCompilersResponse: + type: object + properties: + zkCompilers: + type: array + items: + type: string + solcCompilers: + type: array + items: + type: string + solidityMatch: + type: object + properties: + type: + $ref: '#/definitions/solidityMatchMatchType' + values: + type: string + transformations: + type: string + solidityMatchMatchType: + type: string + enum: + - MATCH_TYPE_UNSPECIFIED + - PARTIAL + - FULL + default: MATCH_TYPE_UNSPECIFIED + solidityVerificationFailure: + type: object + properties: + message: + type: string + solidityVerificationSuccess: + type: object + properties: + fileName: + type: string + contractName: + type: string + zkCompiler: + type: string + zkCompilerVersion: + type: string + evmCompiler: + type: string + evmCompilerVersion: + type: string + language: + type: string + compilerSettings: + type: string + sources: + type: object + additionalProperties: + type: string + compilationArtifacts: + type: string + creationCodeArtifacts: + type: string + runtimeCodeArtifacts: + type: string + creationMatch: + $ref: '#/definitions/solidityMatch' + runtimeMatch: + $ref: '#/definitions/solidityMatch' + solidityVerifyStandardJsonRequest: + type: object + properties: + code: + type: string + constructorArguments: + type: string + zkCompiler: + type: string + solcCompiler: + type: string + input: + type: string v2BatchVerifyResponse: type: object properties: compilationFailure: - $ref: '#/definitions/v2CompilationFailure' + $ref: '#/definitions/smartContractVerifierv2CompilationFailure' contractVerificationResults: $ref: '#/definitions/BatchVerifyResponseContractVerificationResults' v2BatchVerifySolidityMultiPartRequest: @@ -417,11 +548,6 @@ definitions: - CREATION_INPUT - DEPLOYED_BYTECODE default: BYTECODE_TYPE_UNSPECIFIED - v2CompilationFailure: - type: object - properties: - message: - type: string v2Contract: type: object properties: @@ -599,19 +725,6 @@ definitions: title: |- / The chain (network) the contract was deployed to / (https://docs.sourcify.dev/docs/api/chains/) - v2VerifyResponse: - type: object - properties: - message: - type: string - status: - $ref: '#/definitions/v2VerifyResponseStatus' - source: - $ref: '#/definitions/v2Source' - extraData: - $ref: '#/definitions/VerifyResponseExtraData' - postActionResponses: - $ref: '#/definitions/VerifyResponsePostActionResponses' v2VerifyResponseStatus: type: string enum: @@ -756,3 +869,17 @@ definitions: metadata: $ref: '#/definitions/v2VerificationMetadata' title: / An optional field to be filled by explorers + zksyncsolidityCompilationFailure: + type: object + properties: + message: + type: string + zksyncsolidityVerifyResponse: + type: object + properties: + compilationFailure: + $ref: '#/definitions/zksyncsolidityCompilationFailure' + verificationFailure: + $ref: '#/definitions/solidityVerificationFailure' + verificationSuccess: + $ref: '#/definitions/solidityVerificationSuccess' diff --git a/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml b/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml index d54e79946..50926c967 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml +++ b/smart-contract-verifier/smart-contract-verifier-server/Cargo.toml @@ -12,35 +12,32 @@ sig-provider-extension = { path = "../sig-provider-extension", optional = true } actix-web = "4" actix-web-prom = "0.6" -amplify = { version = "3.13.0", features = ["derive"] } +amplify = { version = "4.6.0", features = ["derive"] } anyhow = "1.0" async-trait = "0.1" -blockscout-display-bytes = { version = "1.0" } +blockscout-display-bytes = { workspace = true } blockscout-service-launcher = { workspace = true } bytes = "1.3" config = "0.13" cron = "0.11" ethers-solc = "2.0.10" ethers-core = "2.0.10" -foundry-compilers = "0.3.9" +foundry-compilers = "=0.3.9" futures = "0.3" hex = "0.4.3" lazy_static = "1" -opentelemetry = { version = "0.18", features = ["rt-tokio"] } -opentelemetry-jaeger = { version = "0.17", features = ["rt-tokio"] } prometheus = "0.13" rust-s3 = "0.32" serde = "1.0" serde_json = "1.0" -serde_with = "2.1" +serde_with = "3.8.1" thiserror = "1.0" tokio = { version = "1", features = ["rt-multi-thread"] } tonic = "0.8" tracing = "0.1" -tracing-opentelemetry = "0.18" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } url = "2.3" uuid = { version = "1.6.1", features = ["v4"] } +verification-common = { workspace = true } [dev-dependencies] ethers-solc = { version = "2.0.10", features = ["svm-solc"] } @@ -48,5 +45,6 @@ blockscout-service-launcher = { workspace = true , features = ["test-server"]} ethabi = "18.0" pretty_assertions = "1.3" reqwest = "0.11.13" -rstest = "0.18" -stdext = "0.3.2" \ No newline at end of file +rstest = "0.19.0" +stdext = "0.3.2" +tempfile = "3.10.1" \ No newline at end of file diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/run.rs b/smart-contract-verifier/smart-contract-verifier-server/src/run.rs index 567b15a16..60a2a50a6 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/run.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/run.rs @@ -1,14 +1,21 @@ use crate::{ proto::{ - health_actix::route_health, health_server::HealthServer, + health_actix::route_health, + health_server::HealthServer, solidity_verifier_actix::route_solidity_verifier, solidity_verifier_server::SolidityVerifierServer, sourcify_verifier_actix::route_sourcify_verifier, sourcify_verifier_server::SourcifyVerifierServer, - vyper_verifier_actix::route_vyper_verifier, vyper_verifier_server::VyperVerifierServer, + vyper_verifier_actix::route_vyper_verifier, + vyper_verifier_server::VyperVerifierServer, + zksync::solidity::{ + zk_sync_solidity_verifier_actix::route_zk_sync_solidity_verifier, + zk_sync_solidity_verifier_server::ZkSyncSolidityVerifierServer, + }, }, services::{ - HealthService, SolidityVerifierService, SourcifyVerifierService, VyperVerifierService, + zksync_solidity_verifier, HealthService, SolidityVerifierService, SourcifyVerifierService, + VyperVerifierService, }, settings::Settings, }; @@ -21,6 +28,7 @@ struct HttpRouter { solidity_verifier: Option>, vyper_verifier: Option>, sourcify_verifier: Option>, + zksync_solidity_verifier: Option>, health: Arc, } @@ -44,6 +52,13 @@ impl launcher::HttpRouter for HttpRouter { } else { service_config }; + let service_config = if let Some(zksync_solidity) = &self.zksync_solidity_verifier { + service_config.configure(|config| { + route_zk_sync_solidity_verifier(config, zksync_solidity.clone()) + }) + } else { + service_config + }; let _ = service_config; } @@ -53,6 +68,7 @@ fn grpc_router( solidity_verifier: Option>, vyper_verifier: Option>, sourcify_verifier: Option>, + zksync_solidity_verifier: Option>, health: Arc, ) -> tonic::transport::server::Router { tonic::transport::Server::builder() @@ -60,6 +76,7 @@ fn grpc_router( .add_optional_service(solidity_verifier.map(SolidityVerifierServer::from_arc)) .add_optional_service(vyper_verifier.map(VyperVerifierServer::from_arc)) .add_optional_service(sourcify_verifier.map(SourcifyVerifierServer::from_arc)) + .add_optional_service(zksync_solidity_verifier.map(ZkSyncSolidityVerifierServer::from_arc)) } pub async fn run(settings: Settings) -> Result<(), anyhow::Error> { @@ -93,17 +110,29 @@ pub async fn run(settings: Settings) -> Result<(), anyhow::Error> { )), false => None, }; + let zksync_solidity_verifier = match settings.zksync_solidity.enabled { + true => Some(Arc::new( + zksync_solidity_verifier::Service::new( + settings.zksync_solidity, + compilers_lock.clone(), + ) + .await?, + )), + false => None, + }; let health = Arc::new(HealthService::default()); let grpc_router = grpc_router( solidity_verifier.clone(), vyper_verifier.clone(), sourcify_verifier.clone(), + zksync_solidity_verifier.clone(), health.clone(), ); let http_router = HttpRouter { solidity_verifier, vyper_verifier, sourcify_verifier, + zksync_solidity_verifier, health, }; let launch_settings = LaunchSettings { diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/common.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/common.rs new file mode 100644 index 000000000..1b847602e --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/common.rs @@ -0,0 +1,72 @@ +use crate::settings::{FetcherSettings, S3FetcherSettings}; +use cron::Schedule; +use s3::{creds::Credentials, Bucket, Region}; +use smart_contract_verifier::{Fetcher, FileValidator, ListFetcher, S3Fetcher, Version}; +use std::{path::PathBuf, str::FromStr, sync::Arc}; + +pub async fn initialize_fetcher( + fetcher_settings: FetcherSettings, + compilers_dir: PathBuf, + schedule: Schedule, + validator: Option>>, +) -> anyhow::Result>> +where + ::Err: std::fmt::Display, +{ + let fetcher: Arc> = match fetcher_settings { + FetcherSettings::List(list_settings) => Arc::new( + ListFetcher::new( + list_settings.list_url, + compilers_dir, + Some(schedule), + validator, + ) + .await?, + ), + FetcherSettings::S3(s3_settings) => Arc::new( + S3Fetcher::new( + new_bucket(&s3_settings)?, + compilers_dir, + Some(schedule), + validator, + ) + .await?, + ), + }; + + Ok(fetcher) +} + +fn new_region(region: Option, endpoint: Option) -> Option { + let region = region.unwrap_or_default(); + if let Some(endpoint) = endpoint { + return Some(Region::Custom { region, endpoint }); + } + + // try to match with AWS regions, fail otherwise + let region = Region::from_str(®ion).ok()?; + match region { + Region::Custom { + region: _, + endpoint: _, + } => None, + region => Some(region), + } +} + +fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result> { + let region = new_region(settings.region.clone(), settings.endpoint.clone()) + .ok_or_else(|| anyhow::anyhow!("got invalid region/endpoint settings"))?; + let bucket = Arc::new(Bucket::new( + &settings.bucket, + region, + Credentials::new( + settings.access_key.as_deref(), + settings.secret_key.as_deref(), + None, + None, + None, + )?, + )?); + Ok(bucket) +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/mod.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/mod.rs index aa7daaef0..5d4d24ffa 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/mod.rs @@ -1,7 +1,9 @@ +mod common; mod health; mod solidity_verifier; mod sourcify_verifier; mod vyper_verifier; +pub mod zksync_solidity_verifier; pub use health::HealthService; pub use solidity_verifier::SolidityVerifierService; diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs index bc203c114..8afe0caa4 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/solidity_verifier.rs @@ -6,7 +6,8 @@ use crate::{ ListCompilerVersionsRequest, ListCompilerVersionsResponse, VerifyResponse, VerifySolidityMultiPartRequest, VerifySolidityStandardJsonRequest, }, - settings::{Extensions, FetcherSettings, S3FetcherSettings, SoliditySettings}, + services::common, + settings::{Extensions, SoliditySettings}, types, types::{ LookupMethodsRequestWrapper, LookupMethodsResponseWrapper, StandardJsonParseError, @@ -14,15 +15,15 @@ use crate::{ VerifySolidityStandardJsonRequestWrapper, }, }; -use s3::{creds::Credentials, Bucket, Region}; +use anyhow::Context; use smart_contract_verifier::{ - find_methods, solidity, Compilers, Fetcher, ListFetcher, S3Fetcher, SolcValidator, - SolidityClient, SolidityCompiler, VerificationError, + find_methods, solidity, Compilers, SolcValidator, SolidityClient, SolidityCompiler, + VerificationError, }; use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{ BytecodeType, LookupMethodsRequest, LookupMethodsResponse, }; -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use tokio::sync::Semaphore; use tonic::{Request, Response, Status}; @@ -37,35 +38,21 @@ impl SolidityVerifierService { /* Otherwise, results in compilation warning if all extensions are disabled */ #[allow(unused_variables)] extensions: Extensions, ) -> anyhow::Result { - let dir = settings.compilers_dir.clone(); - let schedule = settings.refresh_versions_schedule; - let validator = Arc::new(SolcValidator::default()); - let fetcher: Arc = match settings.fetcher { - FetcherSettings::List(list_settings) => Arc::new( - ListFetcher::new( - list_settings.list_url, - settings.compilers_dir, - Some(schedule), - Some(validator), - ) - .await?, - ), - FetcherSettings::S3(s3_settings) => Arc::new( - S3Fetcher::new( - new_bucket(&s3_settings)?, - settings.compilers_dir, - Some(schedule), - Some(validator), - ) - .await?, - ), - }; + let solc_validator = Arc::new(SolcValidator::default()); + let fetcher = common::initialize_fetcher( + settings.fetcher, + settings.compilers_dir.clone(), + settings.refresh_versions_schedule, + Some(solc_validator), + ) + .await + .context("solidity fetcher initialization")?; let compilers = Compilers::new( fetcher, SolidityCompiler::new(), compilers_threads_semaphore, ); - compilers.load_from_dir(&dir).await; + compilers.load_from_dir(&settings.compilers_dir).await; /* Otherwise, results in compilation warning if all extensions are disabled */ #[allow(unused_mut)] @@ -321,37 +308,3 @@ impl SolidityVerifier for SolidityVerifierService { Ok(Response::new(response.into())) } } - -fn new_region(region: Option, endpoint: Option) -> Option { - let region = region.unwrap_or_default(); - if let Some(endpoint) = endpoint { - return Some(Region::Custom { region, endpoint }); - } - - // try to match with AWS regions, fail otherwise - let region = Region::from_str(®ion).ok()?; - match region { - Region::Custom { - region: _, - endpoint: _, - } => None, - region => Some(region), - } -} - -fn new_bucket(settings: &S3FetcherSettings) -> anyhow::Result> { - let region = new_region(settings.region.clone(), settings.endpoint.clone()) - .ok_or_else(|| anyhow::anyhow!("got invalid region/endpoint settings"))?; - let bucket = Arc::new(Bucket::new( - &settings.bucket, - region, - Credentials::new( - settings.access_key.as_deref(), - settings.secret_key.as_deref(), - None, - None, - None, - )?, - )?); - Ok(bucket) -} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs index b102f768c..1aada964e 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/vyper_verifier.rs @@ -5,15 +5,15 @@ use crate::{ ListCompilerVersionsResponse, VerifyResponse, VerifyVyperMultiPartRequest, VerifyVyperStandardJsonRequest, }, - settings::{Extensions, FetcherSettings, VyperSettings}, + services::common, + settings::{Extensions, VyperSettings}, types::{ StandardJsonParseError, VerifyResponseWrapper, VerifyVyperMultiPartRequestWrapper, VerifyVyperStandardJsonRequestWrapper, }, }; -use smart_contract_verifier::{ - vyper, Compilers, ListFetcher, VerificationError, VyperClient, VyperCompiler, -}; +use anyhow::Context; +use smart_contract_verifier::{vyper, Compilers, VerificationError, VyperClient, VyperCompiler}; use std::sync::Arc; use tokio::sync::Semaphore; use tonic::{Request, Response, Status}; @@ -29,24 +29,16 @@ impl VyperVerifierService { /* Otherwise, results in compilation warning if all extensions are disabled */ #[allow(unused_variables)] extensions: Extensions, ) -> anyhow::Result { - let dir = settings.compilers_dir.clone(); - let list_url = match settings.fetcher { - FetcherSettings::List(s) => s.list_url, - FetcherSettings::S3(_) => { - return Err(anyhow::anyhow!("S3 fetcher for vyper not supported")) - } - }; - let fetcher = Arc::new( - ListFetcher::new( - list_url, - settings.compilers_dir, - Some(settings.refresh_versions_schedule), - None, - ) - .await?, - ); + let fetcher = common::initialize_fetcher( + settings.fetcher, + settings.compilers_dir.clone(), + settings.refresh_versions_schedule, + None, + ) + .await + .context("vyper fetcher initialization")?; let compilers = Compilers::new(fetcher, VyperCompiler::new(), compilers_threads_semaphore); - compilers.load_from_dir(&dir).await; + compilers.load_from_dir(&settings.compilers_dir).await; /* Otherwise, results in compilation warning if all extensions are disabled */ #[allow(unused_mut)] diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/services/zksync_solidity_verifier.rs b/smart-contract-verifier/smart-contract-verifier-server/src/services/zksync_solidity_verifier.rs new file mode 100644 index 000000000..41a717548 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/src/services/zksync_solidity_verifier.rs @@ -0,0 +1,175 @@ +use crate::{ + proto::zksync::solidity::{ + verify_response, zk_sync_solidity_verifier_server::ZkSyncSolidityVerifier, + CompilationFailure, ListCompilersRequest, ListCompilersResponse, VerificationFailure, + VerificationSuccess, VerifyResponse, VerifyStandardJsonRequest, + }, + services::common, + settings::ZksyncSoliditySettings, + types::{zksolc_standard_json::VerifyStandardJsonRequestWrapper, StandardJsonParseError}, +}; +use anyhow::Context; +use smart_contract_verifier::{ + zksync, + zksync::{ZkSolcCompiler, ZkSyncCompilers}, + SolcValidator, +}; +use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::zksync::solidity::{ + r#match::MatchType, Match, +}; +use std::sync::Arc; +use tokio::sync::Semaphore; +use tonic::{Request, Response, Status}; +use verification_common::verifier_alliance; + +pub struct Service { + compilers: ZkSyncCompilers, +} + +impl Service { + pub async fn new( + settings: ZksyncSoliditySettings, + compilers_threads_semaphore: Arc, + ) -> anyhow::Result { + let solc_validator = Arc::new(SolcValidator::default()); + let evm_fetcher = common::initialize_fetcher( + settings.evm_fetcher, + settings.evm_compilers_dir.clone(), + settings.evm_refresh_versions_schedule, + Some(solc_validator), + ) + .await + .context("zksync solc fetcher initialization")?; + + let zk_fetcher = common::initialize_fetcher( + settings.zk_fetcher, + settings.zk_compilers_dir.clone(), + settings.zk_refresh_versions_schedule, + None, + ) + .await + .context("zksync zksolc fetcher initialization")?; + + let compilers = ZkSyncCompilers::new( + evm_fetcher.clone(), + zk_fetcher.clone(), + compilers_threads_semaphore, + ); + + Ok(Self { compilers }) + } +} + +#[async_trait::async_trait] +impl ZkSyncSolidityVerifier for Service { + async fn verify_standard_json( + &self, + request: Request, + ) -> Result, Status> { + let request: VerifyStandardJsonRequestWrapper = request.into_inner().into(); + + let verification_request: zksync::VerificationRequest = { + let request: Result<_, StandardJsonParseError> = request.try_into(); + if let Err(err) = request { + return match err { + StandardJsonParseError::InvalidContent(_) => { + let response = compilation_failure(format!("Invalid standard json: {err}")); + Ok(Response::new(response)) + } + StandardJsonParseError::BadRequest(_) => { + tracing::info!(err=%err, "Bad request"); + Err(Status::invalid_argument(err.to_string())) + } + }; + } + request.unwrap() + }; + + let result = zksync::verify(&self.compilers, verification_request).await; + + let response = process_verification_result(result)?; + Ok(Response::new(response)) + } + + async fn list_compilers( + &self, + _request: Request, + ) -> Result, Status> { + Ok(Response::new(ListCompilersResponse { + solc_compilers: self.compilers.all_evm_versions_sorted_str(), + zk_compilers: self.compilers.all_zk_versions_sorted_str(), + })) + } +} + +fn process_verification_result( + result: Result, +) -> Result { + match result { + Ok(result) => match zksync::choose_best_success(result.successes) { + Some(success) => { + let proto_success = VerificationSuccess { + file_name: success.file_path, + contract_name: success.contract_name, + zk_compiler: result.zk_compiler, + zk_compiler_version: result.zk_compiler_version.to_string(), + evm_compiler: result.evm_compiler, + evm_compiler_version: result.evm_compiler_version.to_string(), + language: result.language, + compiler_settings: result.compiler_settings.to_string(), + sources: result.sources, + compilation_artifacts: serde_json::Value::from(success.compilation_artifacts) + .to_string(), + creation_code_artifacts: serde_json::Value::from( + success.creation_code_artifacts, + ) + .to_string(), + runtime_code_artifacts: serde_json::Value::from(success.runtime_code_artifacts) + .to_string(), + creation_match: success.creation_match.map(process_match), + runtime_match: Some(process_match(success.runtime_match)), + }; + + Ok(VerifyResponse { + verify_response: Some(verify_response::VerifyResponse::VerificationSuccess( + proto_success, + )), + }) + } + None => Ok(VerifyResponse { + verify_response: Some(verify_response::VerifyResponse::VerificationFailure( + VerificationFailure { + message: "No contract could be verified with provided data".to_string(), + }, + )), + }), + }, + Err(ref err) => match err { + zksync::Error::Compilation(_) => Ok(compilation_failure(err.to_string())), + zksync::Error::ZkCompilerNotFound(_) | zksync::Error::EvmCompilerNotFound(_) => { + Err(Status::invalid_argument(err.to_string())) + } + zksync::Error::Internal(_) => Err(Status::internal(err.to_string())), + }, + } +} + +fn process_match(internal_match: verifier_alliance::Match) -> Match { + let match_type = match internal_match.r#type { + verifier_alliance::MatchType::Full => MatchType::Full, + verifier_alliance::MatchType::Partial => MatchType::Partial, + }; + Match { + r#type: match_type.into(), + values: serde_json::Value::from(internal_match.values).to_string(), + transformations: serde_json::Value::from(internal_match.transformations).to_string(), + } +} + +fn compilation_failure(message: String) -> VerifyResponse { + VerifyResponse { + verify_response: Some(verify_response::VerifyResponse::CompilationFailure( + CompilationFailure { message }, + )), + } +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/settings.rs b/smart-contract-verifier/smart-contract-verifier-server/src/settings.rs index c981c7407..d508835f9 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/settings.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/settings.rs @@ -8,6 +8,7 @@ use serde::Deserialize; use serde_with::{serde_as, DisplayFromStr}; use smart_contract_verifier::{ DEFAULT_SOLIDITY_COMPILER_LIST, DEFAULT_SOURCIFY_HOST, DEFAULT_VYPER_COMPILER_LIST, + DEFAULT_ZKSOLC_COMPILER_LIST, }; use std::{ num::{NonZeroU32, NonZeroUsize}, @@ -23,6 +24,7 @@ pub struct Settings { pub solidity: SoliditySettings, pub vyper: VyperSettings, pub sourcify: SourcifySettings, + pub zksync_solidity: ZksyncSoliditySettings, pub metrics: MetricsSettings, pub jaeger: JaegerSettings, pub tracing: TracingSettings, @@ -43,13 +45,11 @@ pub struct SoliditySettings { impl Default for SoliditySettings { fn default() -> Self { - let mut default_dir = std::env::temp_dir(); - default_dir.push("solidity-compilers"); Self { enabled: true, - compilers_dir: default_dir, - refresh_versions_schedule: Schedule::from_str("0 0 * * * * *").unwrap(), // every hour - fetcher: Default::default(), + compilers_dir: default_compilers_dir("solidity-compilers"), + refresh_versions_schedule: schedule_every_hour(), + fetcher: default_list_fetcher(DEFAULT_SOLIDITY_COMPILER_LIST), } } } @@ -67,16 +67,11 @@ pub struct VyperSettings { impl Default for VyperSettings { fn default() -> Self { - let mut default_dir = std::env::temp_dir(); - default_dir.push("vyper-compilers"); - let fetcher = FetcherSettings::List(ListFetcherSettings { - list_url: Url::try_from(DEFAULT_VYPER_COMPILER_LIST).expect("valid url"), - }); Self { enabled: true, - compilers_dir: default_dir, - refresh_versions_schedule: Schedule::from_str("0 0 * * * * *").unwrap(), // every hour - fetcher, + compilers_dir: default_compilers_dir("vyper-compilers"), + refresh_versions_schedule: schedule_every_hour(), + fetcher: default_list_fetcher(DEFAULT_VYPER_COMPILER_LIST), } } } @@ -88,26 +83,12 @@ pub enum FetcherSettings { S3(S3FetcherSettings), } -impl Default for FetcherSettings { - fn default() -> Self { - Self::List(Default::default()) - } -} - #[derive(Deserialize, Clone, PartialEq, Eq, Debug)] -#[serde(default, deny_unknown_fields)] +#[serde(deny_unknown_fields)] pub struct ListFetcherSettings { pub list_url: Url, } -impl Default for ListFetcherSettings { - fn default() -> Self { - Self { - list_url: Url::try_from(DEFAULT_SOLIDITY_COMPILER_LIST).expect("valid url"), - } - } -} - #[derive(Deserialize, Default, Clone, PartialEq, Eq, Debug)] #[serde(deny_unknown_fields)] pub struct S3FetcherSettings { @@ -140,6 +121,35 @@ impl Default for SourcifySettings { } } +#[serde_as] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct ZksyncSoliditySettings { + pub enabled: bool, + pub evm_compilers_dir: PathBuf, + #[serde_as(as = "DisplayFromStr")] + pub evm_refresh_versions_schedule: Schedule, + pub evm_fetcher: FetcherSettings, + pub zk_compilers_dir: PathBuf, + #[serde_as(as = "DisplayFromStr")] + pub zk_refresh_versions_schedule: Schedule, + pub zk_fetcher: FetcherSettings, +} + +impl Default for ZksyncSoliditySettings { + fn default() -> Self { + Self { + enabled: true, + evm_compilers_dir: default_compilers_dir("zksync-solc-compilers"), + evm_refresh_versions_schedule: schedule_every_hour(), + evm_fetcher: default_list_fetcher(DEFAULT_SOLIDITY_COMPILER_LIST), + zk_compilers_dir: default_compilers_dir("zksync-zksolc-compilers"), + zk_refresh_versions_schedule: schedule_every_hour(), + zk_fetcher: default_list_fetcher(DEFAULT_ZKSOLC_COMPILER_LIST), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] #[serde(default, deny_unknown_fields)] pub struct CompilersSettings { @@ -185,3 +195,19 @@ impl ConfigSettings for Settings { Ok(()) } } + +fn default_compilers_dir>(path: P) -> PathBuf { + let mut compilers_dir = std::env::temp_dir(); + compilers_dir.push(path); + compilers_dir +} + +fn default_list_fetcher(list_url: &str) -> FetcherSettings { + FetcherSettings::List(ListFetcherSettings { + list_url: Url::try_from(list_url).expect("invalid default list.json url"), + }) +} + +fn schedule_every_hour() -> Schedule { + Schedule::from_str("0 0 * * * * *").unwrap() +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/batch_verification.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/batch_verification.rs index f5dfeaabd..17bea30d2 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/batch_verification.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/batch_verification.rs @@ -1,4 +1,4 @@ -use smart_contract_verifier::{solidity, BatchError, BatchVerificationResult, Version}; +use smart_contract_verifier::{solidity, BatchError, BatchVerificationResult, DetailedVersion}; use smart_contract_verifier_proto::blockscout::smart_contract_verifier::{ v2 as proto, v2::{BatchVerifyResponse, CompilationFailure}, @@ -26,8 +26,8 @@ pub fn from_proto_contracts_to_inner( Ok(inner_contracts) } -pub fn from_proto_compiler_version_to_inner(proto: &str) -> Result { - Version::from_str(proto) +pub fn from_proto_compiler_version_to_inner(proto: &str) -> Result { + DetailedVersion::from_str(proto) .map_err(|err| Status::invalid_argument(format!("Invalid compiler version: {}", err))) } diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs index 04526b64d..fc1d65eda 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/lookup_methods.rs @@ -13,14 +13,14 @@ impl TryFrom for LookupMethodsRequest { type Error = tonic::Status; fn try_from(request: LookupMethodsRequestWrapper) -> Result { - let bytecode = DisplayBytes::from_str(&request.bytecode) + let bytecode = DisplayBytes::from_str(&request.0.bytecode) .map_err(|e| tonic::Status::invalid_argument(format!("Invalid bytecode: {e:?}")))? .0; - let abi = Abi::load(request.abi.as_bytes()) + let abi = Abi::load(request.0.abi.as_bytes()) .map_err(|e| tonic::Status::invalid_argument(format!("Invalid abi: {e:?}")))?; - let source_map = sourcemap::parse(&request.source_map) + let source_map = sourcemap::parse(&request.0.source_map) .map_err(|e| tonic::Status::invalid_argument(format!("Invalid source_map: {e:?}")))?; - let file_ids = request.file_ids.clone(); + let file_ids = request.0.file_ids.clone(); Ok(Self { bytecode, diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs index 9aa1986cc..2ca59c697 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/mod.rs @@ -7,6 +7,7 @@ mod sourcify_from_etherscan; mod verify_response; mod vyper_multi_part; mod vyper_standard_json; +pub mod zksolc_standard_json; pub mod batch_verification; mod lookup_methods; diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_multi_part.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_multi_part.rs index 6b2fa6ce7..966492413 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_multi_part.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_multi_part.rs @@ -4,7 +4,7 @@ use foundry_compilers::EvmVersion; use serde::{Deserialize, Serialize}; use smart_contract_verifier::{ solidity::multi_part::{MultiFileContent, VerificationRequest}, - Version, + DetailedVersion, }; use std::{collections::BTreeMap, ops::Deref, path::PathBuf, str::FromStr}; @@ -52,9 +52,10 @@ impl TryFrom for VerificationRequest { BytecodeType::DeployedBytecode => (None, bytecode), }; - let compiler_version = Version::from_str(&request.compiler_version).map_err(|err| { - tonic::Status::invalid_argument(format!("Invalid compiler version: {err}")) - })?; + let compiler_version = + DetailedVersion::from_str(&request.compiler_version).map_err(|err| { + tonic::Status::invalid_argument(format!("Invalid compiler version: {err}")) + })?; let sources: BTreeMap = request .source_files @@ -117,7 +118,7 @@ mod tests { let mut expected = VerificationRequest { creation_bytecode: Some(DisplayBytes::from_str("0x1234").unwrap().0), deployed_bytecode: DisplayBytes::from_str("").unwrap().0, - compiler_version: Version::from_str("v0.8.17+commit.8df45f5f").unwrap(), + compiler_version: DetailedVersion::from_str("v0.8.17+commit.8df45f5f").unwrap(), content: MultiFileContent { sources: BTreeMap::from([("source_path".into(), "source_content".into())]), evm_version: Some(EvmVersion::London), diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_standard_json.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_standard_json.rs index ce9244ae3..b5fa834d3 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_standard_json.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/solidity_standard_json.rs @@ -6,7 +6,7 @@ use foundry_compilers::CompilerInput; use serde::{Deserialize, Serialize}; use smart_contract_verifier::{ solidity::standard_json::{StandardJsonContent, VerificationRequest}, - Version, + DetailedVersion, }; use std::{ops::Deref, str::FromStr}; @@ -53,7 +53,7 @@ impl TryFrom for VerificationRequest { BytecodeType::CreationInput => (Some(bytecode), bytes::Bytes::new()), BytecodeType::DeployedBytecode => (None, bytecode), }; - let compiler_version = Version::from_str(&request.compiler_version) + let compiler_version = DetailedVersion::from_str(&request.compiler_version) .map_err(|err| anyhow!("Invalid compiler version: {}", err))?; let input: CompilerInput = serde_json::from_str(&request.input)?; @@ -99,7 +99,7 @@ mod tests { let mut expected = VerificationRequest { creation_bytecode: Some(DisplayBytes::from_str("0x1234").unwrap().0), deployed_bytecode: DisplayBytes::from_str("").unwrap().0, - compiler_version: Version::from_str("v0.8.17+commit.8df45f5f").unwrap(), + compiler_version: DetailedVersion::from_str("v0.8.17+commit.8df45f5f").unwrap(), content: StandardJsonContent { input }, chain_id: Some("1".into()), }; diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/source.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/source.rs index 7fecd3833..389aa3afe 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/source.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/source.rs @@ -123,7 +123,7 @@ mod tests { CompilerInput, EvmVersion, }; use pretty_assertions::assert_eq; - use smart_contract_verifier::{vyper, Version}; + use smart_contract_verifier::{vyper, DetailedVersion}; use std::{collections::BTreeMap, str::FromStr}; #[test] @@ -150,7 +150,7 @@ mod tests { settings: compiler_settings.clone(), }, compiler_output: Default::default(), - compiler_version: Version::from_str("v0.8.17+commit.8df45f5f").unwrap(), + compiler_version: DetailedVersion::from_str("v0.8.17+commit.8df45f5f").unwrap(), file_path: "file_name".to_string(), contract_name: "contract_name".to_string(), abi: Some(serde_json::Value::Object(Default::default())), @@ -206,7 +206,7 @@ mod tests { settings: compiler_settings.clone(), }, compiler_output: Default::default(), - compiler_version: Version::from_str("v0.3.9+commit.66b96705").unwrap(), + compiler_version: DetailedVersion::from_str("v0.3.9+commit.66b96705").unwrap(), file_path: "file_name".to_string(), contract_name: "contract_name".to_string(), abi: Some(serde_json::Value::Object(Default::default())), diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs index f6880fe2a..0781cc003 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/verify_response.rs @@ -166,7 +166,7 @@ mod tests { use blockscout_display_bytes::Bytes as DisplayBytes; use foundry_compilers::CompilerInput; use pretty_assertions::assert_eq; - use smart_contract_verifier::{MatchType, SoliditySuccess, Version}; + use smart_contract_verifier::{DetailedVersion, MatchType, SoliditySuccess}; use std::str::FromStr; #[test] @@ -178,7 +178,7 @@ mod tests { settings: Default::default(), }, compiler_output: Default::default(), - compiler_version: Version::from_str("v0.8.17+commit.8df45f5f").unwrap(), + compiler_version: DetailedVersion::from_str("v0.8.17+commit.8df45f5f").unwrap(), file_path: "file_path".to_string(), contract_name: "contract_name".to_string(), abi: None, diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_multi_part.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_multi_part.rs index 98a80cf26..bd3c2deb9 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_multi_part.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_multi_part.rs @@ -4,7 +4,7 @@ use foundry_compilers::EvmVersion; use serde::{Deserialize, Serialize}; use smart_contract_verifier::{ vyper::multi_part::{MultiFileContent, VerificationRequest}, - Version, + DetailedVersion, }; use std::{collections::BTreeMap, ops::Deref, path::PathBuf, str::FromStr}; @@ -51,9 +51,10 @@ impl TryFrom for VerificationRequest { BytecodeType::CreationInput => (Some(bytecode), bytes::Bytes::new()), BytecodeType::DeployedBytecode => (None, bytecode), }; - let compiler_version = Version::from_str(&request.compiler_version).map_err(|err| { - tonic::Status::invalid_argument(format!("Invalid compiler version: {err}")) - })?; + let compiler_version = + DetailedVersion::from_str(&request.compiler_version).map_err(|err| { + tonic::Status::invalid_argument(format!("Invalid compiler version: {err}")) + })?; let sources: BTreeMap = request .source_files @@ -122,7 +123,7 @@ mod tests { let expected = VerificationRequest { creation_bytecode: Some(DisplayBytes::from_str("0x1234").unwrap().0), deployed_bytecode: DisplayBytes::from_str("").unwrap().0, - compiler_version: Version::from_str("0.3.7+commit.6020b8bb").unwrap(), + compiler_version: DetailedVersion::from_str("0.3.7+commit.6020b8bb").unwrap(), content: MultiFileContent { sources: BTreeMap::from([("source_path".into(), "source_content".into())]), interfaces: BTreeMap::from([("interface_path".into(), "interface_content".into())]), diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_standard_json.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_standard_json.rs index 0404b26fe..518af30e6 100644 --- a/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_standard_json.rs +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/vyper_standard_json.rs @@ -8,7 +8,7 @@ use smart_contract_verifier::{ artifacts::CompilerInput, standard_json::{StandardJsonContent, VerificationRequest}, }, - Version, + DetailedVersion, }; use std::{ops::Deref, str::FromStr}; @@ -55,7 +55,7 @@ impl TryFrom for VerificationRequest { BytecodeType::CreationInput => (Some(bytecode), bytes::Bytes::new()), BytecodeType::DeployedBytecode => (None, bytecode), }; - let compiler_version = Version::from_str(&request.compiler_version) + let compiler_version = DetailedVersion::from_str(&request.compiler_version) .map_err(|err| anyhow!("Invalid compiler version: {}", err))?; let input: CompilerInput = serde_json::from_str(&request.input)?; @@ -100,7 +100,7 @@ mod tests { let mut expected = VerificationRequest { creation_bytecode: Some(DisplayBytes::from_str("0x1234").unwrap().0), deployed_bytecode: DisplayBytes::from_str("").unwrap().0, - compiler_version: Version::from_str("v0.3.7+commit.6020b8bb").unwrap(), + compiler_version: DetailedVersion::from_str("v0.3.7+commit.6020b8bb").unwrap(), content: StandardJsonContent { input }, chain_id: Some("1".into()), }; diff --git a/smart-contract-verifier/smart-contract-verifier-server/src/types/zksolc_standard_json.rs b/smart-contract-verifier/smart-contract-verifier-server/src/types/zksolc_standard_json.rs new file mode 100644 index 000000000..c7c69673b --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/src/types/zksolc_standard_json.rs @@ -0,0 +1,46 @@ +use super::StandardJsonParseError; +use crate::proto::zksync::solidity::VerifyStandardJsonRequest; +use amplify::{From, Wrapper}; +use anyhow::anyhow; +use blockscout_display_bytes::Bytes as DisplayBytes; +use smart_contract_verifier::{ + zksync::{zksolc_standard_json, VerificationRequest}, + CompactVersion, DetailedVersion, +}; +use std::str::FromStr; + +#[derive(Wrapper, From, Clone, Debug, PartialEq)] +pub struct VerifyStandardJsonRequestWrapper(VerifyStandardJsonRequest); + +impl TryFrom for VerificationRequest { + type Error = StandardJsonParseError; + + fn try_from(request: VerifyStandardJsonRequestWrapper) -> Result { + let request = request.into_inner(); + + let code = DisplayBytes::from_str(&request.code) + .map_err(|err| anyhow!("Invalid deployed bytecode: {:#?}", err))? + .0; + let constructor_arguments = request + .constructor_arguments + .as_deref() + .map(DisplayBytes::from_str) + .transpose() + .map_err(|err| anyhow!("Invalid constructor arguments: {:#?}", err))? + .map(|v| v.0); + let zk_compiler = CompactVersion::from_str(&request.zk_compiler) + .map_err(|err| anyhow!("Invalid zk compiler: {}", err))?; + let solc_compiler = DetailedVersion::from_str(&request.solc_compiler) + .map_err(|err| anyhow!("Invalid solc compiler: {}", err))?; + + let content: zksolc_standard_json::input::Input = serde_json::from_str(&request.input)?; + + Ok(Self { + code, + constructor_arguments, + zk_compiler, + solc_compiler, + content, + }) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_zksync_solidity/simple_standard_json.json b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_zksync_solidity/simple_standard_json.json new file mode 100644 index 000000000..42551dd59 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/test_cases_zksync_solidity/simple_standard_json.json @@ -0,0 +1,31 @@ +{ + "_comment": "A simple contract to check that verification works", + "deployed_code": "0x00050000000000020000008003000039000000400030043f0000000003010019000000600330027000000072033001970000000102200190000000960000c13d000000040230008c000001600000413d000000000201043b000000e002200270000000790420009c0000014e0000613d0000007a0220009c000001600000c13d0000000002000416000000000202004b000001600000c13d000000040230008a000000200220008c000001600000413d0000000402100370000000000502043b000000760250009c000001600000213d00000023025000390000007e04000041000000000632004b000000000600001900000000060480190000007e02200197000000000702004b00000000040080190000007e0220009c000000000406c019000000000204004b000001600000c13d0000000406500039000000000261034f000000000402043b000000760240009c0000009c0000213d000000bf07400039000000200200008a000000000727016f000000760870009c0000009c0000213d000000400070043f000000800040043f00000000054500190000002405500039000000000335004b000001600000213d0000002003600039000000000131034f0000001f0340018f0000000505400272000000440000613d00000000060000190000000507600210000000000871034f000000000808043b000000a00770003900000000008704350000000106600039000000000756004b0000003c0000413d000000000603004b000000530000613d0000000505500210000000000151034f0000000303300210000000a005500039000000000605043300000000063601cf000000000636022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000161019f0000000000150435000000a0014000390000000000010435000000800100043d000000760310009c0000009c0000213d000000000400041a000000010340019000000001054002700000007f0350018f000000000305c0190000001f0530008c00000000050000190000000105002039000000000454013f00000001044001900000015c0000c13d000000200430008c000000750000413d0000001f0410003900000005044002700000007b054000410000007b04000041000000200610008c000000000405801900000000000004350000001f0330003900000005033002700000007b03300041000000000534004b000000750000813d000000000004041b0000000104400039000000000534004b000000710000413d0000001f0310008c0000019f0000a13d0000000003210170000000a0040000390000007b020000410000000000000435000000890000613d0000007b0200004100000020060000390000000004000019000000000506001900000080065000390000000006060433000000000062041b000000200650003900000001022000390000002004400039000000000734004b0000007f0000413d000000a004500039000000000313004b000000930000813d0000000303100210000000f80330018f000000010500008a000000000335022f000000000353013f0000000004040433000000000334016f000000000032041b00000001020000390000000103100210000001a90000013d0000000002000416000000000202004b000001600000c13d0000007302300041000000740220009c000000a20000213d0000007f0100004100000000001004350000004101000039000000040010043f0000008001000041000001c5000104300000009f023000390000007502200197000000400020043f0000001f0230018f0000000504300272000000b10000613d00000000050000190000000506500210000000000761034f000000000707043b000000800660003900000000007604350000000105500039000000000645004b000000a90000413d000000000502004b000000c00000613d0000000504400210000000000141034f00000003022002100000008004400039000000000504043300000000052501cf000000000525022f000000000101043b0000010002200089000000000121022f00000000012101cf000000000151019f0000000000140435000000200130008c000001600000413d000000800400043d000000760140009c000001600000213d00000080033000390000009f01400039000000000131004b000001600000813d00000080024000390000000001020433000000760510009c0000009c0000213d0000003f05100039000000200900008a000000000595016f000000400800043d0000000005580019000000000685004b00000000060000190000000106004039000000760750009c0000009c0000213d00000001066001900000009c0000c13d000000400050043f00000000061804360000000004140019000000a004400039000000000334004b000001600000213d000000000301004b000000e90000613d000000000300001900000000046300190000002003300039000000000523001900000000050504330000000000540435000000000413004b000000e20000413d000000000116001900000000000104350000000004080433000000760140009c0000009c0000213d000000000100041a000000010210019000000001011002700000007f0310018f000000000301c0190000001f0130008c00000000010000190000000101002039000000010110018f000000000112004b0000015c0000c13d000000200130008c0000011f0000413d000100000003001d000300000004001d000000000000043500000072010000410000000002000414000000720320009c0000000002018019000000c00120021000000077011001c70000801002000039000500000008001d000400000009001d000200000006001d01c301be0000040f0000000206000029000000040900002900000005080000290000000102200190000001600000613d00000003040000290000001f024000390000000502200270000000200340008c0000000002004019000000000301043b00000001010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b0000011f0000813d000000000002041b0000000102200039000000000312004b0000011b0000413d0000001f0140008c000001ad0000a13d000300000004001d000000000000043500000072010000410000000002000414000000720320009c0000000002018019000000c00120021000000077011001c70000801002000039000500000008001d000400000009001d01c301be0000040f000000040300002900000005060000290000000102200190000001600000613d000000030700002900000000033701700000002002000039000000000101043b000001400000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000001380000413d000000000373004b0000014b0000813d0000000303700210000000f80330018f000000010400008a000000000334022f000000000343013f00000000026200190000000002020433000000000232016f000000000021041b00000001010000390000000102700210000001b70000013d0000000001000416000000000101004b000001600000c13d000000000100041a000000010310019000000001041002700000007f0240018f000000000204c0190000001f0420008c00000000040000190000000104002039000000000441013f0000000104400190000001620000613d0000007f01000041000000000010043500000022010000390000009f0000013d0000000001000019000001c500010430000000800020043f000000000303004b000001750000613d000000a0010000390000000000000435000000000302004b000001810000613d0000007b0100004100000000040000190000000003040019000000000401041a000000a005300039000000000045043500000001011000390000002004300039000000000524004b0000016b0000413d000000c0013000390000017b0000013d000001000300008a000000000131016f000000a00010043f000000000102004b000000c001000039000000a0010060390000001f01100039000000200200008a000000000121016f0000007c021000410000007d0220009c0000009c0000413d000000400010043f00000020020000390000000003210436000000800200043d00000000002304350000004003100039000000000402004b000001910000613d00000000040000190000000005340019000000a006400039000000000606043300000000006504350000002004400039000000000524004b0000018a0000413d000000000332001900000000000304350000005f02200039000000200300008a000000000232016f0000007203000041000000720420009c0000000002038019000000720410009c000000000103801900000040011002100000006002200210000000000112019f000001c40001042e000000000201004b0000000002000019000001a30000613d000000a00200043d0000000303100210000000010400008a000000000334022f000000000343013f000000000332016f0000000102100210000000000123019f000000000010041b0000000001000019000001c40001042e000000000104004b0000000001000019000001b10000613d00000000010604330000000302400210000000010300008a000000000223022f000000000232013f000000000221016f0000000101400210000000000112019f000000000010041b0000002001000039000001000010044300000120000004430000007801000041000001c40001042e000001c1002104230000000102000039000000000001042d0000000002000019000000000001042d000001c300000432000001c40001042e000001c5000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000009fffffffffffffffffffffffffffffffffffffffffffffffff000000000000007f00000000000000000000000000000000000000000000000000000001ffffffe0000000000000000000000000000000000000000000000000ffffffffffffffff0200000000000000000000000000000000000020000000000000000000000000000000020000000000000000000000000000004000000100000000000000000000000000000000000000000000000000000000000000000000000000cfae321700000000000000000000000000000000000000000000000000000000a4136862290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff000000000000008080000000000000000000000000000000000000000000000000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2b6fe8cd9af668ef698f6e0992d0e9aacae5e06943b1402d755814260f9fdac", + "constructor_arguments": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000", + "zk_compiler_version": "v1.4.0", + "evm_compiler_version": "v0.8.17+commit.8df45f5f", + "input": {"language":"Solidity","settings":{"libraries":{},"optimizer":{"enabled":true,"mode":"3"},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.methodIdentifiers","metadata"]}}},"sources":{"contracts/Greeter.sol":{"content":"//SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function greet() public view returns (string memory) {\n return greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}\n"}}}, + "file_name": "contracts/Greeter.sol", + "contract_name": "Greeter", + "expected_sources": { + "contracts/Greeter.sol": "//SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function greet() public view returns (string memory) {\n return greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}\n" + }, + "expected_compilation_artifacts": {"abi":[{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1},"storageLayout":{"storage":[{"astId":3,"contract":"contracts/Greeter.sol:Greeter","label":"greeting","offset":0,"slot":"0","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}, + "expected_creation_code_artifacts": {}, + "expected_runtime_code_artifacts": {}, + + "expected_creation_match_type": "full", + "expected_creation_transformations": [ + { + "type": "insert", + "reason": "constructor", + "offset": 4192 + } + ], + "expected_creation_values": { + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000" + }, + "expected_runtime_match_type": "full", + "expected_runtime_transformations": [], + "expected_runtime_values": {} +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/main.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/main.rs new file mode 100644 index 000000000..08a084e30 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/main.rs @@ -0,0 +1,53 @@ +use tempfile::TempDir; + +mod types; +mod zksync_solidity; + +struct ServerMetadata { + base_url: url::Url, + _zk_compilers_tempdir: TempDir, + _evm_compilers_tempdir: TempDir, +} + +async fn start() -> ServerMetadata { + let (settings, server) = { + let mut settings = smart_contract_verifier_server::Settings::default(); + let (server_settings, base) = + blockscout_service_launcher::test_server::get_test_server_settings(); + settings.server = server_settings; + + settings.solidity.enabled = false; + settings.vyper.enabled = false; + settings.sourcify.enabled = false; + + let zk_compilers_tempdir = + tempfile::tempdir().expect("creation temporary directory for zk_compilers"); + let evm_compilers_tempdir = + tempfile::tempdir().expect("creation temporary directory for evm_compilers"); + + settings.zksync_solidity.enabled = true; + settings.zksync_solidity.zk_compilers_dir = zk_compilers_tempdir.path().to_path_buf(); + settings.zksync_solidity.evm_compilers_dir = evm_compilers_tempdir.path().to_path_buf(); + + settings.metrics.enabled = false; + settings.tracing.enabled = false; + settings.jaeger.enabled = false; + + ( + settings, + ServerMetadata { + base_url: base, + _zk_compilers_tempdir: zk_compilers_tempdir, + _evm_compilers_tempdir: evm_compilers_tempdir, + }, + ) + }; + + blockscout_service_launcher::test_server::init_server( + || smart_contract_verifier_server::run(settings), + &server.base_url, + ) + .await; + + server +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/types.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/types.rs new file mode 100644 index 000000000..3f4f9277f --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/types.rs @@ -0,0 +1,213 @@ +use bytes::Bytes; +use pretty_assertions::assert_eq; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_json::Value; +use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::zksync::solidity::{ + verify_response, Match, VerificationSuccess, VerifyResponse, VerifyStandardJsonRequest, +}; +use std::collections::BTreeMap; + +pub trait TestCase { + fn to_request(&self) -> VerifyStandardJsonRequest; + fn check_verification_success(&self, success: VerificationSuccess); + + fn check_verify_response(&self, response: VerifyResponse) { + match response { + VerifyResponse { + verify_response: Some(verify_response::VerifyResponse::VerificationSuccess(success)), + } => self.check_verification_success(success), + _ => { + panic!("invalid response: {response:#?}") + } + } + } +} + +pub fn from_file(test_case: &str) -> T { + let current_dir = std::env::current_dir().unwrap(); + let current_dir = current_dir.to_string_lossy(); + let test_case_path = format!("{current_dir}/tests/test_cases_zksync_solidity/{test_case}.json"); + let content = std::fs::read_to_string(test_case_path).expect("failed to read file"); + serde_json::from_str(&content).expect("invalid test case format") +} + +#[serde_with::serde_as] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +pub struct StandardJson { + #[serde_as(as = "blockscout_display_bytes::serde_as::Hex")] + pub deployed_code: Bytes, + #[serde_as(as = "Option")] + pub constructor_arguments: Option, + pub zk_compiler_version: String, + pub evm_compiler_version: String, + pub input: Value, + pub file_name: String, + pub contract_name: String, + pub expected_sources: Option>, + pub expected_compilation_artifacts: Option, + pub expected_creation_code_artifacts: Option, + pub expected_runtime_code_artifacts: Option, + pub expected_creation_match_type: Option, + pub expected_creation_transformations: Option, + pub expected_creation_values: Option, + pub expected_runtime_match_type: Option, + pub expected_runtime_transformations: Option, + pub expected_runtime_values: Option, +} + +impl TestCase for StandardJson { + fn to_request(&self) -> VerifyStandardJsonRequest { + VerifyStandardJsonRequest { + code: hex::encode(&self.deployed_code), + constructor_arguments: self.constructor_arguments.as_ref().map(hex::encode), + zk_compiler: self.zk_compiler_version.clone(), + solc_compiler: self.evm_compiler_version.clone(), + input: self.input.to_string(), + } + } + + fn check_verification_success(&self, success: VerificationSuccess) { + assert_eq!(self.file_name, success.file_name, "invalid file name"); + assert_eq!( + self.contract_name, success.contract_name, + "invalid contract name" + ); + assert_eq!("zksolc", success.zk_compiler, "invalid zk-compiler"); + assert_eq!( + self.zk_compiler_version, success.zk_compiler_version, + "invalid zk-compiler version" + ); + assert_eq!("solc", success.evm_compiler, "invalid evm-compiler"); + assert_eq!( + self.evm_compiler_version, success.evm_compiler_version, + "invalid evm-compiler version" + ); + + { + #[derive(Deserialize)] + struct Input { + language: String, + } + let input = + Input::deserialize(&self.input).expect("expected language field deserialization"); + assert_eq!( + input.language.to_lowercase(), + success.language.to_lowercase(), + "invalid language" + ); + } + + { + #[derive(Deserialize)] + struct Input { + settings: Value, + } + let mut input = Input::deserialize(&self.input) + .expect("expected compiler settings field deserialization"); + let mut compiler_settings: Value = serde_json::from_str(&success.compiler_settings) + .expect("compiler settings deserialization"); + remove_output_selection(&mut input.settings); + remove_output_selection(&mut compiler_settings); + assert_eq!( + input.settings, compiler_settings, + "invalid compiler settings" + ); + } + + if let Some(expected_sources) = &self.expected_sources { + assert_eq!(expected_sources, &success.sources, "invalid sources"); + } + + if let Some(expected_compilation_artifacts) = &self.expected_compilation_artifacts { + let compilation_artifacts: Value = serde_json::from_str(&success.compilation_artifacts) + .expect("compilation artifacts deserialization"); + assert_eq!( + expected_compilation_artifacts, &compilation_artifacts, + "invalid compilation artifacts" + ); + } + + if let Some(expected_creation_code_artifacts) = &self.expected_creation_code_artifacts { + let creation_code_artifacts: Value = + serde_json::from_str(&success.creation_code_artifacts) + .expect("creation code artifacts deserialization"); + assert_eq!( + expected_creation_code_artifacts, &creation_code_artifacts, + "invalid creation code artifacts" + ); + } + + if let Some(expected_runtime_code_artifacts) = &self.expected_runtime_code_artifacts { + let runtime_code_artifacts: Value = + serde_json::from_str(&success.runtime_code_artifacts) + .expect("runtime code artifacts deserialization"); + assert_eq!( + expected_runtime_code_artifacts, &runtime_code_artifacts, + "invalid runtime code artifacts" + ); + } + + check_match( + "creation", + self.expected_creation_match_type.as_ref(), + self.expected_creation_values.as_ref(), + self.expected_creation_transformations.as_ref(), + success.creation_match, + ); + check_match( + "runtime", + self.expected_runtime_match_type.as_ref(), + self.expected_runtime_values.as_ref(), + self.expected_runtime_transformations.as_ref(), + success.runtime_match, + ); + } +} + +fn remove_output_selection(compiler_settings: &mut Value) { + compiler_settings + .as_object_mut() + .expect("Compiler settings is not an object") + .remove("outputSelection"); +} + +fn check_match( + prefix: &'static str, + expected_match_type: Option<&String>, + expected_values: Option<&Value>, + expected_transformations: Option<&Value>, + actual_match: Option, +) { + match (expected_match_type, actual_match) { + (None, None) => {} + (Some(expected_creation_match_type), Some(actual_match)) => { + assert_eq!( + expected_creation_match_type.to_lowercase(), + actual_match.r#type().as_str_name().to_lowercase(), + "invalid {} match type", + prefix, + ); + if let Some(expected_values) = expected_values { + let values: Value = serde_json::from_str(&actual_match.values) + .unwrap_or_else(|_| panic!("{prefix} values deserialization")); + assert_eq!(expected_values, &values, "invalid {} values", prefix,); + } + if let Some(expected_transformations) = expected_transformations { + let transformations: Value = serde_json::from_str(&actual_match.transformations) + .unwrap_or_else(|_| panic!("{prefix} transformations deserialization")); + assert_eq!( + expected_transformations, &transformations, + "invalid {} transformations", + prefix, + ); + } + } + (expected, actual) => { + panic!( + "invalid {prefix} match type; expected={:?}; actual={:?}", + expected.as_ref().map(|v| v.to_lowercase()), + actual.map(|v| v.r#type().as_str_name().to_lowercase()) + ) + } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/zksync_solidity.rs b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/zksync_solidity.rs new file mode 100644 index 000000000..ba8b654a1 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier-server/tests/zksync_integration/zksync_solidity.rs @@ -0,0 +1,88 @@ +use crate::{ + types, + types::{StandardJson, TestCase}, +}; +use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::zksync::solidity::{ + verify_response, CompilationFailure, ListCompilersResponse, VerifyResponse, + VerifyStandardJsonRequest, +}; + +#[tokio::test] +async fn list_compilers() { + const ROUTE: &str = "/api/v2/zksync-verifier/solidity/versions"; + + let server = super::start().await; + + let response: ListCompilersResponse = + blockscout_service_launcher::test_server::send_get_request(&server.base_url, ROUTE).await; + + // The list of compilers is progressively updated and depends on the fetcher type. + // So here we will just check that one of the latest releases is available. + + assert!( + response + .solc_compilers + .contains(&"v0.8.25+commit.b61c2a91".to_string()), + "solc_compiler v0.8.25 is missed; response={:#?}", + response.solc_compilers, + ); + + assert!( + response.zk_compilers.contains(&"v1.4.1".to_string()), + "zk_compiler v1.4.1 is missed; response={:#?}", + response.zk_compilers, + ) +} + +#[tokio::test] +async fn simple_standard_json() { + const ROUTE: &str = "/api/v2/zksync-verifier/solidity/sources:verify-standard-json"; + + let test_case = types::from_file::("simple_standard_json"); + + let server = super::start().await; + + let request = test_case.to_request(); + let response: VerifyResponse = blockscout_service_launcher::test_server::send_post_request( + &server.base_url, + ROUTE, + &request, + ) + .await; + + test_case.check_verify_response(response); +} + +#[tokio::test] +async fn cannot_compile() { + const ROUTE: &str = "/api/v2/zksync-verifier/solidity/sources:verify-standard-json"; + + let server = super::start().await; + + let request = VerifyStandardJsonRequest { + code: "0x00050000000000020000008003000039000000400030043f0000000003010019000000600330027000000072033001970000000102200190000000960000c13d000000040230008c000001600000413d000000000201043b000000e002200270000000790420009c0000014e0000613d0000007a0220009c000001600000c13d0000000002000416000000000202004b000001600000c13d000000040230008a000000200220008c000001600000413d0000000402100370000000000502043b000000760250009c000001600000213d00000023025000390000007e04000041000000000632004b000000000600001900000000060480190000007e02200197000000000702004b00000000040080190000007e0220009c000000000406c019000000000204004b000001600000c13d0000000406500039000000000261034f000000000402043b000000760240009c0000009c0000213d000000bf07400039000000200200008a000000000727016f000000760870009c0000009c0000213d000000400070043f000000800040043f00000000054500190000002405500039000000000335004b000001600000213d0000002003600039000000000131034f0000001f0340018f0000000505400272000000440000613d00000000060000190000000507600210000000000871034f000000000808043b000000a00770003900000000008704350000000106600039000000000756004b0000003c0000413d000000000603004b000000530000613d0000000505500210000000000151034f0000000303300210000000a005500039000000000605043300000000063601cf000000000636022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000161019f0000000000150435000000a0014000390000000000010435000000800100043d000000760310009c0000009c0000213d000000000400041a000000010340019000000001054002700000007f0350018f000000000305c0190000001f0530008c00000000050000190000000105002039000000000454013f00000001044001900000015c0000c13d000000200430008c000000750000413d0000001f0410003900000005044002700000007b054000410000007b04000041000000200610008c000000000405801900000000000004350000001f0330003900000005033002700000007b03300041000000000534004b000000750000813d000000000004041b0000000104400039000000000534004b000000710000413d0000001f0310008c0000019f0000a13d0000000003210170000000a0040000390000007b020000410000000000000435000000890000613d0000007b0200004100000020060000390000000004000019000000000506001900000080065000390000000006060433000000000062041b000000200650003900000001022000390000002004400039000000000734004b0000007f0000413d000000a004500039000000000313004b000000930000813d0000000303100210000000f80330018f000000010500008a000000000335022f000000000353013f0000000004040433000000000334016f000000000032041b00000001020000390000000103100210000001a90000013d0000000002000416000000000202004b000001600000c13d0000007302300041000000740220009c000000a20000213d0000007f0100004100000000001004350000004101000039000000040010043f0000008001000041000001c5000104300000009f023000390000007502200197000000400020043f0000001f0230018f0000000504300272000000b10000613d00000000050000190000000506500210000000000761034f000000000707043b000000800660003900000000007604350000000105500039000000000645004b000000a90000413d000000000502004b000000c00000613d0000000504400210000000000141034f00000003022002100000008004400039000000000504043300000000052501cf000000000525022f000000000101043b0000010002200089000000000121022f00000000012101cf000000000151019f0000000000140435000000200130008c000001600000413d000000800400043d000000760140009c000001600000213d00000080033000390000009f01400039000000000131004b000001600000813d00000080024000390000000001020433000000760510009c0000009c0000213d0000003f05100039000000200900008a000000000595016f000000400800043d0000000005580019000000000685004b00000000060000190000000106004039000000760750009c0000009c0000213d00000001066001900000009c0000c13d000000400050043f00000000061804360000000004140019000000a004400039000000000334004b000001600000213d000000000301004b000000e90000613d000000000300001900000000046300190000002003300039000000000523001900000000050504330000000000540435000000000413004b000000e20000413d000000000116001900000000000104350000000004080433000000760140009c0000009c0000213d000000000100041a000000010210019000000001011002700000007f0310018f000000000301c0190000001f0130008c00000000010000190000000101002039000000010110018f000000000112004b0000015c0000c13d000000200130008c0000011f0000413d000100000003001d000300000004001d000000000000043500000072010000410000000002000414000000720320009c0000000002018019000000c00120021000000077011001c70000801002000039000500000008001d000400000009001d000200000006001d01c301be0000040f0000000206000029000000040900002900000005080000290000000102200190000001600000613d00000003040000290000001f024000390000000502200270000000200340008c0000000002004019000000000301043b00000001010000290000001f01100039000000050110027000000000011300190000000002230019000000000312004b0000011f0000813d000000000002041b0000000102200039000000000312004b0000011b0000413d0000001f0140008c000001ad0000a13d000300000004001d000000000000043500000072010000410000000002000414000000720320009c0000000002018019000000c00120021000000077011001c70000801002000039000500000008001d000400000009001d01c301be0000040f000000040300002900000005060000290000000102200190000001600000613d000000030700002900000000033701700000002002000039000000000101043b000001400000613d0000002002000039000000000400001900000000056200190000000005050433000000000051041b000000200220003900000001011000390000002004400039000000000534004b000001380000413d000000000373004b0000014b0000813d0000000303700210000000f80330018f000000010400008a000000000334022f000000000343013f00000000026200190000000002020433000000000232016f000000000021041b00000001010000390000000102700210000001b70000013d0000000001000416000000000101004b000001600000c13d000000000100041a000000010310019000000001041002700000007f0240018f000000000204c0190000001f0420008c00000000040000190000000104002039000000000441013f0000000104400190000001620000613d0000007f01000041000000000010043500000022010000390000009f0000013d0000000001000019000001c500010430000000800020043f000000000303004b000001750000613d000000a0010000390000000000000435000000000302004b000001810000613d0000007b0100004100000000040000190000000003040019000000000401041a000000a005300039000000000045043500000001011000390000002004300039000000000524004b0000016b0000413d000000c0013000390000017b0000013d000001000300008a000000000131016f000000a00010043f000000000102004b000000c001000039000000a0010060390000001f01100039000000200200008a000000000121016f0000007c021000410000007d0220009c0000009c0000413d000000400010043f00000020020000390000000003210436000000800200043d00000000002304350000004003100039000000000402004b000001910000613d00000000040000190000000005340019000000a006400039000000000606043300000000006504350000002004400039000000000524004b0000018a0000413d000000000332001900000000000304350000005f02200039000000200300008a000000000232016f0000007203000041000000720420009c0000000002038019000000720410009c000000000103801900000040011002100000006002200210000000000112019f000001c40001042e000000000201004b0000000002000019000001a30000613d000000a00200043d0000000303100210000000010400008a000000000334022f000000000343013f000000000332016f0000000102100210000000000123019f000000000010041b0000000001000019000001c40001042e000000000104004b0000000001000019000001b10000613d00000000010604330000000302400210000000010300008a000000000223022f000000000232013f000000000221016f0000000101400210000000000112019f000000000010041b0000002001000039000001000010044300000120000004430000007801000041000001c40001042e000001c1002104230000000102000039000000000001042d0000000002000019000000000001042d000001c300000432000001c40001042e000001c5000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000009fffffffffffffffffffffffffffffffffffffffffffffffff000000000000007f00000000000000000000000000000000000000000000000000000001ffffffe0000000000000000000000000000000000000000000000000ffffffffffffffff0200000000000000000000000000000000000020000000000000000000000000000000020000000000000000000000000000004000000100000000000000000000000000000000000000000000000000000000000000000000000000cfae321700000000000000000000000000000000000000000000000000000000a4136862290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff000000000000008080000000000000000000000000000000000000000000000000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d2b6fe8cd9af668ef698f6e0992d0e9aacae5e06943b1402d755814260f9fdac".to_string(), + constructor_arguments: Some("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000".to_string()), + zk_compiler: "v1.4.0".to_string(), + solc_compiler: "v0.8.17+commit.8df45f5f".to_string(), + input: "{\"language\":\"Solidity\",\"settings\":{\"libraries\":{},\"optimizer\":{\"enabled\":true,\"mode\":\"3\"},\"outputSelection\":{\"*\":{\"\":[\"ast\"],\"*\":[\"abi\",\"evm.methodIdentifiers\",\"metadata\"]}}},\"sources\":{\"contracts/Greeter.sol\":{\"content\":\"Some content that will not compile\"}}}".to_string(), + }; + + let response: VerifyResponse = blockscout_service_launcher::test_server::send_post_request( + &server.base_url, + ROUTE, + &request, + ) + .await; + + match response { + VerifyResponse { + verify_response: + Some(verify_response::VerifyResponse::CompilationFailure(CompilationFailure { + message: _message, + })), + } => {} + _ => { + panic!("invalid response: {response:#?}") + } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/Cargo.toml b/smart-contract-verifier/smart-contract-verifier/Cargo.toml index ade209cca..bb951ebdc 100644 --- a/smart-contract-verifier/smart-contract-verifier/Cargo.toml +++ b/smart-contract-verifier/smart-contract-verifier/Cargo.toml @@ -11,19 +11,19 @@ alloy-dyn-abi = "0.6.4" alloy-json-abi = "0.6.4" anyhow = "1.0" async-trait = "0.1" -blockscout-display-bytes = { version = "1.0" } +blockscout-display-bytes = { workspace = true } bytes = "1.2" chrono = "0.4" cron = "0.11" ethabi = "18.0" ethers-core = "2.0.10" ethers-solc = { version = "2.0.6", features = ["async"] } -foundry-compilers = "0.3.9" +foundry-compilers = { version = "=0.3.9", features = ["async"] } futures = "0.3" hex = "0.4" lazy_static = "1" -minicbor = { version = "0.18", features = ["std"] } mismatch = "1.0" +nonempty = "0.10.0" parking_lot = "0.12" primitive-types = "0.12" prometheus = "0.13" @@ -36,15 +36,17 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" serde_with = "2" sha2 = "0.10" -solidity-metadata = "1.0" -sourcify = { git = "https://github.com/blockscout/blockscout-rs", rev = "457af68" } +solidity-metadata = "1.1.0" +sourcify = { git = "https://github.com/blockscout/blockscout-rs", rev = "c4261d0" } sscanf = "0.3" tempfile = "3.3" thiserror = "1.0" tokio = { version = "1", features = ["macros"] } tracing = "0.1" url = { version = "2.4", features = ["serde"] } -verification-common = { git = "https://github.com/blockscout/blockscout-rs", rev = "7a6e940" } +verification-common = { workspace = true } + +era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", rev = "9969de1" } [dev-dependencies] const_format = "0.2" diff --git a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/batch_contract_verifier.rs b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/batch_contract_verifier.rs index f6ac4bd3b..325b5d841 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/batch_contract_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/batch_contract_verifier.rs @@ -2,34 +2,15 @@ use crate::{ batch_verifier::{ artifacts::{cbor_auxdata, CodeArtifacts}, compilation::{self, CompilationResult}, - errors::{VerificationError, VerificationErrorKind}, + errors::{BatchError, VerificationError, VerificationErrorKind}, transformations, }, - compiler::{self, CompilerInput}, - Compilers, Contract, MatchType, SolidityCompiler, Version, + compiler::CompilerInput, + Compilers, Contract, DetailedVersion, MatchType, SolidityCompiler, }; use std::collections::BTreeMap; -use thiserror::Error; -#[derive(Error, Debug)] -pub enum BatchError { - #[error("Compiler version not found: {0}")] - VersionNotFound(Version), - #[error("Compilation error: {0:?}")] - Compilation(Vec), - #[error("{0}")] - Internal(anyhow::Error), -} - -impl From for BatchError { - fn from(error: compiler::Error) -> Self { - match error { - compiler::Error::VersionNotFound(version) => BatchError::VersionNotFound(version), - compiler::Error::Compilation(details) => BatchError::Compilation(details), - err => BatchError::Internal(anyhow::anyhow!(err)), - } - } -} +pub type VerificationResult = crate::batch_verifier::VerificationResult; #[derive(Clone, Debug)] pub struct Match { @@ -38,12 +19,6 @@ pub struct Match { pub transformations: serde_json::Value, } -#[derive(Debug)] -pub enum VerificationResult { - Success(BatchSuccess), - Failure(Vec), -} - #[derive(Clone, Debug, Default)] pub struct BatchSuccess { pub compiler: String, @@ -64,7 +39,7 @@ pub struct BatchSuccess { pub async fn verify_solidity( compilers: &Compilers, - compiler_version: Version, + compiler_version: DetailedVersion, contracts: Vec, compiler_input: &foundry_compilers::CompilerInput, ) -> Result, BatchError> { diff --git a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/compilation.rs b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/compilation.rs index f1d717982..7e00fa3b7 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/compilation.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/compilation.rs @@ -1,5 +1,5 @@ use super::artifacts::{self}; -use crate::{verifier::lossless_compiler_output, Version}; +use crate::{verifier::lossless_compiler_output, DetailedVersion}; use anyhow::Context; use artifacts::LinkReferences; use bytes::Bytes; @@ -40,7 +40,7 @@ mod solidity { use artifacts::cbor_auxdata::{self, CborAuxdata}; pub fn parse_contracts( - compiler_version: Version, + compiler_version: DetailedVersion, compiler_input: &foundry_compilers::CompilerInput, compiler_output: serde_json::Value, modified_compiler_output: serde_json::Value, diff --git a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/errors.rs b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/errors.rs index 80e29fc56..d66071611 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/errors.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/errors.rs @@ -1,6 +1,27 @@ +use crate::compiler; use std::fmt::{Display, Formatter}; use thiserror::Error; +#[derive(Error, Debug)] +pub enum BatchError { + #[error("Compiler version not found: {0}")] + VersionNotFound(String), + #[error("Compilation error: {0:?}")] + Compilation(Vec), + #[error("{0:#}")] + Internal(anyhow::Error), +} + +impl From for BatchError { + fn from(error: compiler::Error) -> Self { + match error { + compiler::Error::VersionNotFound(version) => BatchError::VersionNotFound(version), + compiler::Error::Compilation(details) => BatchError::Compilation(details), + err => BatchError::Internal(anyhow::anyhow!(err)), + } + } +} + /// Enumerates errors that may occur during a single contract verification. #[derive(Error, Debug)] pub enum VerificationErrorKind { diff --git a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/mod.rs index e0e745fb7..9a1f67385 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/batch_verifier/mod.rs @@ -5,9 +5,16 @@ mod errors; mod transformations; pub use batch_contract_verifier::{ - verify_solidity, BatchError, BatchSuccess, Match as BatchMatch, + verify_solidity, BatchSuccess, Match as BatchMatch, VerificationResult as BatchVerificationResult, }; +pub use errors::BatchError; + +#[derive(Debug)] +pub enum VerificationResult { + Success(Success), + Failure(Vec), +} pub fn decode_hex(value: &str) -> Result, hex::FromHexError> { if let Some(value) = value.strip_prefix("0x") { diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/compilers.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/compilers.rs index e0d53dbcb..d68246b7f 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/compilers.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/compilers.rs @@ -1,7 +1,7 @@ use super::{ download_cache::DownloadCache, fetcher::{FetchError, Fetcher}, - version::Version, + version_detailed::DetailedVersion, }; use crate::metrics::{self, GuardedGauge}; use ethers_solc::{artifacts::Severity, error::SolcError, CompilerOutput}; @@ -17,7 +17,7 @@ use tracing::instrument; #[derive(Debug, Error)] pub enum Error { #[error("Compiler version not found: {0}")] - VersionNotFound(Version), + VersionNotFound(String), #[error("Error while fetching compiler: {0:#}")] Fetch(#[from] FetchError), #[error("Internal error while compiling: {0}")] @@ -33,7 +33,7 @@ pub trait CompilerInput { /// should have modified metadata hash, if any. fn modify(self) -> Self; - fn normalize_output_selection(&mut self, version: &Version); + fn normalize_output_selection(&mut self, version: &DetailedVersion); } #[async_trait::async_trait] @@ -43,14 +43,14 @@ pub trait EvmCompiler { async fn compile( &self, path: &Path, - ver: &Version, + ver: &DetailedVersion, input: &Self::CompilerInput, ) -> Result<(serde_json::Value, CompilerOutput), SolcError>; } pub struct Compilers { - cache: DownloadCache, - fetcher: Arc, + cache: DownloadCache, + fetcher: Arc>, evm_compiler: C, threads_semaphore: Arc, } @@ -60,12 +60,12 @@ where C: EvmCompiler, { pub fn new( - fetcher: Arc, + fetcher: Arc>, evm_compiler: C, threads_semaphore: Arc, ) -> Self { Self { - cache: DownloadCache::new(), + cache: Default::default(), fetcher, evm_compiler, threads_semaphore, @@ -74,7 +74,7 @@ where #[instrument(name = "download_and_compile", skip(self, input), level = "debug")] pub async fn compile( &self, - compiler_version: &Version, + compiler_version: &DetailedVersion, input: &C::CompilerInput, chain_id: Option<&str>, ) -> Result<(serde_json::Value, CompilerOutput), Error> { @@ -129,7 +129,7 @@ where Ok((raw, output)) } - pub fn all_versions(&self) -> Vec { + pub fn all_versions(&self) -> Vec { self.fetcher.all_versions() } @@ -156,7 +156,7 @@ where #[cfg(test)] mod tests { - use super::{super::list_fetcher::ListFetcher, *}; + use super::{super::ListFetcher, *}; use crate::{consts::DEFAULT_SOLIDITY_COMPILER_LIST, solidity::SolidityCompiler}; use foundry_compilers::{ artifacts::{Source, Sources}, @@ -227,7 +227,8 @@ mod tests { let compilers = global_compilers().await; let input: CompilerInput = Input::with_source_code(source_code.into()).into(); - let version = Version::from_str("v0.8.10+commit.fc410830").expect("Compiler version"); + let version = + DetailedVersion::from_str("v0.8.10+commit.fc410830").expect("Compiler version"); let (_raw, result) = compilers .compile(&version, &input, None) @@ -245,7 +246,8 @@ mod tests { let compilers = global_compilers().await; let input: CompilerInput = Input::with_source_code(source_code.into()).into(); - let version = Version::from_str("v0.5.9+commit.c68bc34e").expect("Compiler version"); + let version = + DetailedVersion::from_str("v0.5.9+commit.c68bc34e").expect("Compiler version"); let (_raw, result) = compilers .compile(&version, &input, None) @@ -263,7 +265,8 @@ mod tests { let compilers = global_compilers().await; let input: CompilerInput = Input::with_source_code(source_code.into()).into(); - let version = Version::from_str("v0.8.10+commit.fc410830").expect("Compiler version"); + let version = + DetailedVersion::from_str("v0.8.10+commit.fc410830").expect("Compiler version"); let result = compilers .compile(&version, &input, None) diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/download_cache.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/download_cache.rs index 52debcd1d..937451f76 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/download_cache.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/download_cache.rs @@ -1,24 +1,22 @@ -use super::{ - fetcher::{FetchError, Fetcher}, - version::Version, -}; +use super::fetcher::{FetchError, Fetcher, Version}; use crate::metrics; -use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; use tracing::Instrument; -#[derive(Default)] -pub struct DownloadCache { - cache: parking_lot::Mutex>>>>, +pub struct DownloadCache { + cache: parking_lot::Mutex>>>>, } -impl DownloadCache { - pub fn new() -> Self { - DownloadCache { - cache: Default::default(), +impl Default for DownloadCache { + fn default() -> Self { + Self { + cache: parking_lot::Mutex::new(HashMap::new()), } } +} - async fn try_get(&self, ver: &Version) -> Option { +impl DownloadCache { + async fn try_get(&self, ver: &Ver) -> Option { let entry = { let cache = self.cache.lock(); cache.get(ver).cloned() @@ -33,11 +31,11 @@ impl DownloadCache { } } -impl DownloadCache { - pub async fn get( +impl DownloadCache { + pub async fn get + ?Sized>( &self, fetcher: &D, - ver: &Version, + ver: &Ver, ) -> Result { metrics::DOWNLOAD_CACHE_TOTAL.inc(); match self.try_get(ver).await { @@ -53,10 +51,10 @@ impl DownloadCache { } } - async fn fetch( + async fn fetch + ?Sized>( &self, fetcher: &D, - ver: &Version, + ver: &Ver, ) -> Result { let lock = { let mut cache = self.cache.lock(); @@ -75,29 +73,13 @@ impl DownloadCache { } pub async fn load_from_dir(&self, dir: &PathBuf) -> std::io::Result<()> { - let paths = DownloadCache::read_dir_paths(dir)?; - let versions = DownloadCache::filter_versions(paths); + let paths = read_dir_paths(dir)?; + let versions = filter_versions(paths); self.add_versions(versions).await; Ok(()) } - fn read_dir_paths(dir: &PathBuf) -> std::io::Result> { - let paths = std::fs::read_dir(dir)?.filter_map(|r| r.ok().map(|e| e.path())); - Ok(paths) - } - - fn filter_versions(dirs: impl Iterator) -> HashMap { - dirs.filter_map(|path| { - path.file_name() - .and_then(|n| n.to_str()) - .map(String::from) - .and_then(|n| Version::from_str(&n).ok()) - .map(|v| (v, path)) - }) - .collect() - } - - async fn add_versions(&self, versions: HashMap) { + async fn add_versions(&self, versions: HashMap) { for (version, path) in versions { let solc_path = path.join("solc"); if solc_path.exists() { @@ -118,21 +100,37 @@ impl DownloadCache { } } +fn read_dir_paths(dir: &PathBuf) -> std::io::Result> { + let paths = std::fs::read_dir(dir)?.filter_map(|r| r.ok().map(|e| e.path())); + Ok(paths) +} + +fn filter_versions(dirs: impl Iterator) -> HashMap { + dirs.filter_map(|path| { + path.file_name() + .and_then(|n| n.to_str()) + .map(String::from) + .and_then(|n| Ver::from_str(&n).ok()) + .map(|v| (v, path)) + }) + .collect() +} + #[cfg(test)] mod tests { use super::{ - super::{list_fetcher::ListFetcher, version::ReleaseVersion}, + super::{fetcher_list::ListFetcher, version_detailed as evm_version}, *, }; use crate::consts::DEFAULT_SOLIDITY_COMPILER_LIST; use async_trait::async_trait; use futures::{executor::block_on, join, pin_mut}; use pretty_assertions::assert_eq; - use std::{collections::HashSet, env::temp_dir, time::Duration}; + use std::{collections::HashSet, env::temp_dir, str::FromStr, time::Duration}; use tokio::{spawn, task::yield_now, time::timeout}; - fn new_version(major: u64) -> Version { - Version::Release(ReleaseVersion { + fn new_version(major: u64) -> evm_version::DetailedVersion { + evm_version::DetailedVersion::Release(evm_version::ReleaseVersion { version: semver::Version::new(major, 0, 0), commit: "00010203".to_string(), }) @@ -143,27 +141,29 @@ mod tests { fn value_is_cached() { #[derive(Default)] struct MockFetcher { - counter: parking_lot::Mutex>, + counter: parking_lot::Mutex>, } #[async_trait] impl Fetcher for MockFetcher { - async fn fetch(&self, ver: &Version) -> Result { + type Version = evm_version::DetailedVersion; + + async fn fetch(&self, ver: &Self::Version) -> Result { *self.counter.lock().entry(ver.clone()).or_default() += 1; Ok(PathBuf::from(ver.to_string())) } - fn all_versions(&self) -> Vec { + fn all_versions(&self) -> Vec { vec![] } } let fetcher = MockFetcher::default(); - let cache = DownloadCache::new(); + let cache = DownloadCache::default(); let vers: Vec<_> = (0..3).map(new_version).collect(); - let get_and_check = |ver: &Version| { + let get_and_check = |ver: &evm_version::DetailedVersion| { let value = block_on(cache.get(&fetcher, ver)).unwrap(); assert_eq!(value, PathBuf::from(ver.to_string())); }; @@ -197,19 +197,21 @@ mod tests { #[async_trait] impl Fetcher for MockBlockingFetcher { - async fn fetch(&self, ver: &Version) -> Result { + type Version = evm_version::DetailedVersion; + + async fn fetch(&self, ver: &Self::Version) -> Result { let _guard = self.sync.lock().await; Ok(PathBuf::from(ver.to_string())) } - fn all_versions(&self) -> Vec { + fn all_versions(&self) -> Vec { vec![] } } let sync = Arc::>::default(); let fetcher = MockBlockingFetcher { sync: sync.clone() }; - let cache = Arc::new(DownloadCache::new()); + let cache = Arc::new(DownloadCache::default()); let vers: Vec<_> = (0..3).map(new_version).collect(); @@ -258,7 +260,8 @@ mod tests { #[tokio::test] async fn filter_versions() { - let versions: HashSet = vec![1, 2, 3, 4, 5].into_iter().map(new_version).collect(); + let versions: HashSet = + vec![1, 2, 3, 4, 5].into_iter().map(new_version).collect(); let paths = versions.iter().map(|v| v.to_string().into()).chain(vec![ "some_random_dir".into(), @@ -267,14 +270,14 @@ mod tests { "�0.7.0+commit.9e61f92b".into(), ]); - let versions_map = DownloadCache::filter_versions(paths); + let versions_map = super::filter_versions(paths); let filtered_versions = HashSet::from_iter(versions_map.into_keys()); assert_eq!(versions, filtered_versions,); } #[tokio::test] async fn load_downloaded_compiler() { - let ver = Version::from_str("0.7.0+commit.9e61f92b").unwrap(); + let ver = evm_version::DetailedVersion::from_str("0.7.0+commit.9e61f92b").unwrap(); let dir = temp_dir(); let url = DEFAULT_SOLIDITY_COMPILER_LIST @@ -285,7 +288,7 @@ mod tests { .expect("Fetch releases"); fetcher.fetch(&ver).await.expect("download should complete"); - let cache = DownloadCache::new(); + let cache = DownloadCache::default(); cache .load_from_dir(&dir) .await diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher.rs index 27ce42cdc..cb58d3002 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher.rs @@ -1,14 +1,16 @@ -use super::version::Version; use async_trait::async_trait; use bytes::Bytes; use mismatch::Mismatch; use primitive_types::H256; use sha2::{Digest, Sha256}; use std::{ + fmt::{Debug, Display}, fs::{File, OpenOptions}, + hash::Hash, io::ErrorKind, os::unix::prelude::OpenOptionsExt, path::{Path, PathBuf}, + str::FromStr, }; use thiserror::Error; use tracing::instrument; @@ -16,7 +18,7 @@ use tracing::instrument; #[derive(Error, Debug)] pub enum FetchError { #[error("version {0} not found")] - NotFound(Version), + NotFound(String), #[error("couldn't fetch the file: {0}")] Fetch(anyhow::Error), #[error("hashsum of fetched file mismatch: {0}")] @@ -32,14 +34,27 @@ pub enum FetchError { } #[async_trait] -pub trait FileValidator: Send + Sync { - async fn validate(&self, ver: &Version, path: &Path) -> Result<(), anyhow::Error>; +pub trait FileValidator: Send + Sync { + async fn validate(&self, ver: &Ver, path: &Path) -> Result<(), anyhow::Error>; } #[async_trait] pub trait Fetcher: Send + Sync { - async fn fetch(&self, ver: &Version) -> Result; - fn all_versions(&self) -> Vec; + type Version; + async fn fetch(&self, ver: &Self::Version) -> Result; + fn all_versions(&self) -> Vec; +} + +pub trait Version: + Clone + Debug + Display + FromStr + PartialEq + Eq + Hash + Send + Sync + 'static +{ + fn to_semver(&self) -> &semver::Version; +} + +impl Version for semver::Version { + fn to_semver(&self) -> &semver::Version { + self + } } #[cfg(target_family = "unix")] @@ -63,12 +78,12 @@ pub fn validate_checksum(bytes: &Bytes, expected: H256) -> Result<(), Mismatch( data: Bytes, sha: H256, path: &Path, - ver: &Version, - validator: Option<&dyn FileValidator>, + ver: &Ver, + validator: Option<&dyn FileValidator>, ) -> Result { let folder = path.join(ver.to_string()); let file = folder.join("solc"); @@ -119,7 +134,7 @@ pub async fn write_executable( #[cfg(test)] mod tests { - use super::*; + use super::{super::version_detailed as evm_version, *}; use std::str::FromStr; #[tokio::test] @@ -130,7 +145,7 @@ mod tests { let bytes = Bytes::from_static(data.as_bytes()); let sha = Sha256::digest(data.as_bytes()); - let version = Version::from_str("v0.4.10+commit.f0d539ae").unwrap(); + let version = evm_version::DetailedVersion::from_str("v0.4.10+commit.f0d539ae").unwrap(); let file = write_executable( bytes, H256::from_slice(&sha), @@ -152,7 +167,7 @@ mod tests { let data = "this is a compiler binary"; let bytes = Bytes::from_static(data.as_bytes()); let sha = H256::default(); - let version = Version::from_str("v0.4.10+commit.f0d539ae").unwrap(); + let version = evm_version::DetailedVersion::from_str("v0.4.10+commit.f0d539ae").unwrap(); let err = write_executable(bytes, sha, tmp_dir.path(), &version, None) .await diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/list_fetcher.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_list.rs similarity index 53% rename from smart-contract-verifier/smart-contract-verifier/src/compiler/list_fetcher.rs rename to smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_list.rs index 44ee8eaf3..71b5983d6 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/list_fetcher.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_list.rs @@ -1,18 +1,24 @@ use super::{ - fetcher::{FetchError, Fetcher, FileValidator}, - version::Version, - versions_fetcher::{VersionsFetcher, VersionsRefresher}, + fetcher::{FetchError, Fetcher, FileValidator, Version}, + fetcher_versions::{VersionsFetcher, VersionsRefresher}, }; use async_trait::async_trait; use bytes::Bytes; use cron::Schedule; use primitive_types::H256; -use std::{collections::HashMap, fmt::Debug, path::PathBuf, sync::Arc}; +use std::{ + collections::HashMap, + fmt::{Debug, Display}, + marker::PhantomData, + path::PathBuf, + str::FromStr, + sync::Arc, +}; use thiserror::Error; use tracing::{debug, instrument}; use url::Url; -type VersionsMap = HashMap; +type VersionsMap = HashMap; #[derive(Clone, Debug, PartialEq)] struct FileInfo { @@ -20,22 +26,6 @@ struct FileInfo { pub sha256: H256, } -impl TryFrom<(json::FileInfo, &Url)> for FileInfo { - type Error = url::ParseError; - - fn try_from((file_info, download_url): (json::FileInfo, &Url)) -> Result { - let url = match file_info.path { - json::DownloadPath::Url(url) => url, - // download_url ends with `.../list.json` but join() will replace this with `filename` - json::DownloadPath::Filename(filename) => download_url.join(&filename)?, - }; - Ok(Self { - url, - sha256: file_info.sha256, - }) - } -} - #[derive(Error, Debug)] enum ListError { #[error("fetching list json returned error: {0}")] @@ -46,16 +36,25 @@ enum ListError { Path(url::ParseError), } -struct ListVersionFetcher { +struct ListVersionFetcher { list_url: Url, + _phantom_data: PhantomData, } -impl ListVersionFetcher { - fn new(list_url: Url) -> ListVersionFetcher { - ListVersionFetcher { list_url } +impl ListVersionFetcher { + fn new(list_url: Url) -> Self { + Self { + list_url, + _phantom_data: Default::default(), + } } +} - async fn fetch_json_versions(&self) -> Result { +impl ListVersionFetcher +where + ::Err: Display, +{ + async fn fetch_json_versions(&self) -> Result, ListError> { reqwest::get(self.list_url.as_str()) .await .map_err(ListError::ListJsonFetch)? @@ -64,11 +63,15 @@ impl ListVersionFetcher { .map_err(ListError::ParseListJson) } - fn parse_json_versions(&self, list_json: json::List) -> Result { + fn parse_json_versions( + &self, + list_json: json::List, + ) -> Result, ListError> { let mut versions = HashMap::default(); for json_compiler_info in list_json.builds { let version = json_compiler_info.long_version.clone(); - let file_info = FileInfo::try_from((json_compiler_info, &self.list_url)) + let file_info = json_compiler_info + .into_compact(&self.list_url) .map_err(ListError::Path)?; versions.insert(version, file_info); } @@ -77,8 +80,11 @@ impl ListVersionFetcher { } #[async_trait] -impl VersionsFetcher for ListVersionFetcher { - type Versions = VersionsMap; +impl VersionsFetcher for ListVersionFetcher +where + ::Err: Display, +{ + type Versions = VersionsMap; type Error = ListError; fn len(vers: &Self::Versions) -> usize { @@ -93,18 +99,21 @@ impl VersionsFetcher for ListVersionFetcher { } } -pub struct ListFetcher { - versions: VersionsRefresher, +pub struct ListFetcher { + versions: VersionsRefresher>, folder: PathBuf, - validator: Option>, + validator: Option>>, } -impl ListFetcher { +impl ListFetcher +where + ::Err: Display, +{ pub async fn new( list_url: Url, folder: PathBuf, refresh_schedule: Option, - validator: Option>, + validator: Option>>, ) -> anyhow::Result { let fetcher = Arc::new(ListVersionFetcher::new(list_url)); let versions = VersionsRefresher::new(fetcher, refresh_schedule).await?; @@ -116,13 +125,13 @@ impl ListFetcher { } #[instrument(skip(self), level = "debug")] - async fn fetch_file(&self, ver: &Version) -> Result<(Bytes, H256), FetchError> { + async fn fetch_file(&self, ver: &Ver) -> Result<(Bytes, H256), FetchError> { let file_info = { let versions = self.versions.read(); versions .get(ver) .cloned() - .ok_or_else(|| FetchError::NotFound(ver.clone()))? + .ok_or_else(|| FetchError::NotFound(ver.clone().to_string()))? }; let response = reqwest::get(file_info.url) @@ -139,16 +148,21 @@ impl ListFetcher { } #[async_trait] -impl Fetcher for ListFetcher { - async fn fetch(&self, ver: &Version) -> Result { +impl Fetcher for ListFetcher +where + ::Err: Display, +{ + type Version = Ver; + + async fn fetch(&self, ver: &Self::Version) -> Result { let (data, hash) = self.fetch_file(ver).await?; super::fetcher::write_executable(data, hash, &self.folder, ver, self.validator.as_deref()) .await } - fn all_versions(&self) -> Vec { + fn all_versions(&self) -> Vec { let versions = self.versions.read(); - versions.iter().map(|(ver, _)| ver.clone()).collect() + versions.keys().map(Clone::clone).collect() } } @@ -157,23 +171,51 @@ mod json { use primitive_types::H256; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; + use std::{fmt::Display, str::FromStr}; use url::Url; #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] - pub struct List { - pub builds: Vec, + pub struct List + where + ::Err: Display, + { + #[serde(bound(deserialize = "Ver: FromStr, Ver::Err: Display"))] + #[serde(bound(serialize = "Ver: Display"))] + pub builds: Vec>, } #[serde_as] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] - pub struct FileInfo { + pub struct FileInfo + where + ::Err: Display, + { pub path: DownloadPath, #[serde_as(as = "DisplayFromStr")] - pub long_version: Version, + #[serde(bound(deserialize = "Ver: FromStr, Ver::Err: Display"))] + #[serde(bound(serialize = "Ver: Display"))] + pub long_version: Ver, pub sha256: H256, } + impl FileInfo + where + ::Err: Display, + { + pub fn into_compact(self, download_url: &Url) -> Result { + let url = match self.path { + DownloadPath::Url(url) => url, + // download_url ends with `.../list.json` but join() will replace this with `filename` + DownloadPath::Filename(filename) => download_url.join(&filename)?, + }; + Ok(super::FileInfo { + url, + sha256: self.sha256, + }) + } + } + #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(untagged)] pub enum DownloadPath { @@ -185,7 +227,10 @@ mod json { #[cfg(test)] mod tests { use super::*; - use crate::{consts::DEFAULT_SOLIDITY_COMPILER_LIST, tests::parse::test_deserialize_ok}; + use crate::{ + consts::DEFAULT_SOLIDITY_COMPILER_LIST, tests::parse::test_deserialize_ok, CompactVersion, + DetailedVersion, + }; use ethers_solc::Solc; use pretty_assertions::assert_eq; use std::{env::temp_dir, str::FromStr}; @@ -232,48 +277,48 @@ mod tests { #[test] fn parse_list_json() { - let ver = |s| Version::from_str(s).unwrap(); + let ver = |s| DetailedVersion::from_str(s).unwrap(); test_deserialize_ok(vec![ (DEFAULT_LIST_JSON, - json::List { - builds: vec![ - json::FileInfo { - path: json::DownloadPath::Url(Url::from_str("https://github.com/blockscout/solc-bin/releases/download/solc-v0.8.15-nightly.2022.5.27%2Bcommit.095cc647/solc").unwrap()), - long_version: ver("0.8.15-nightly.2022.5.27+commit.095cc647"), - sha256: H256::from_str("35708c1593f3daddae734065e361a839ee39d400825972fb3f50718495be82b1").unwrap(), - }, - json::FileInfo { - path: json::DownloadPath::Url(Url::from_str("https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.4.13+commit.0fb4cb1a").unwrap()), - long_version: ver("0.4.13+commit.0fb4cb1a"), - sha256: H256::from_str("0x791ee3a20adf6c5ab76cc889f13cca102f76eb0b7cf0da4a0b5b11dc46edf349").unwrap(), - }, - json::FileInfo { - path: json::DownloadPath::Url(Url::from_str("https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.4.14+commit.c2215d46").unwrap()), - long_version: ver("0.4.14+commit.c2215d46"), - sha256: H256::from_str("0x28ce35a0941d9ecd59a2b1a377c019110e79a6b38bdbf5a3bffea811f9c2a13b").unwrap(), - }, - json::FileInfo { - path: json::DownloadPath::Filename("solc-linux-amd64-v0.4.15+commit.8b45bddb".to_string()), - long_version: ver("0.4.15+commit.8b45bddb"), - sha256: H256::from_str("0xc71ac6c28bf3b1a425e77e97f5df67a80da3e4c047261875206561c0a110c0cb").unwrap(), - }, - json::FileInfo { - path: json::DownloadPath::Filename("download/files/solc-linux-amd64-v0.4.16+commit.d7661dd9".to_string()), - long_version: ver("0.4.16+commit.d7661dd9"), - sha256: H256::from_str("0x78e0da6cad24ab145a8d17420c4f094c8314418ca23cff4b050bb2bfd36f3af2").unwrap(), - }, - json::FileInfo { - path: json::DownloadPath::Filename("solc-linux-amd64-v10.8.9-nightly.2021.9.11+commit.e5eed63a".to_string()), - long_version: ver("10.8.9-nightly.2021.9.11+commit.e5eed63a"), - sha256: H256::from_str("0x791ee3a20adf6c5ab76cc889f13cca102f76eb0b7cf0da4a0b5b11dc46edf349").unwrap(), - }, - ] - }) + json::List { + builds: vec![ + json::FileInfo { + path: json::DownloadPath::Url(Url::from_str("https://github.com/blockscout/solc-bin/releases/download/solc-v0.8.15-nightly.2022.5.27%2Bcommit.095cc647/solc").unwrap()), + long_version: ver("0.8.15-nightly.2022.5.27+commit.095cc647"), + sha256: H256::from_str("35708c1593f3daddae734065e361a839ee39d400825972fb3f50718495be82b1").unwrap(), + }, + json::FileInfo { + path: json::DownloadPath::Url(Url::from_str("https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.4.13+commit.0fb4cb1a").unwrap()), + long_version: ver("0.4.13+commit.0fb4cb1a"), + sha256: H256::from_str("0x791ee3a20adf6c5ab76cc889f13cca102f76eb0b7cf0da4a0b5b11dc46edf349").unwrap(), + }, + json::FileInfo { + path: json::DownloadPath::Url(Url::from_str("https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.4.14+commit.c2215d46").unwrap()), + long_version: ver("0.4.14+commit.c2215d46"), + sha256: H256::from_str("0x28ce35a0941d9ecd59a2b1a377c019110e79a6b38bdbf5a3bffea811f9c2a13b").unwrap(), + }, + json::FileInfo { + path: json::DownloadPath::Filename("solc-linux-amd64-v0.4.15+commit.8b45bddb".to_string()), + long_version: ver("0.4.15+commit.8b45bddb"), + sha256: H256::from_str("0xc71ac6c28bf3b1a425e77e97f5df67a80da3e4c047261875206561c0a110c0cb").unwrap(), + }, + json::FileInfo { + path: json::DownloadPath::Filename("download/files/solc-linux-amd64-v0.4.16+commit.d7661dd9".to_string()), + long_version: ver("0.4.16+commit.d7661dd9"), + sha256: H256::from_str("0x78e0da6cad24ab145a8d17420c4f094c8314418ca23cff4b050bb2bfd36f3af2").unwrap(), + }, + json::FileInfo { + path: json::DownloadPath::Filename("solc-linux-amd64-v10.8.9-nightly.2021.9.11+commit.e5eed63a".to_string()), + long_version: ver("10.8.9-nightly.2021.9.11+commit.e5eed63a"), + sha256: H256::from_str("0x791ee3a20adf6c5ab76cc889f13cca102f76eb0b7cf0da4a0b5b11dc46edf349").unwrap(), + }, + ] + }) ]); } - fn assert_has_version(versions: &VersionsMap, ver: &str, expect: &str) { - let ver = Version::from_str(ver).unwrap(); + fn assert_has_version(versions: &VersionsMap, ver: &str, expect: &str) { + let ver = DetailedVersion::from_str(ver).unwrap(); let info = versions.get(&ver).unwrap(); let url = info.url.to_string(); assert_eq!(url, expect, "urls don't match"); @@ -281,7 +326,8 @@ mod tests { #[test] fn parse_versions() { - let list_json_file: json::List = serde_json::from_str(DEFAULT_LIST_JSON).unwrap(); + let list_json_file: json::List = + serde_json::from_str(DEFAULT_LIST_JSON).unwrap(); let download_url = Url::from_str(DEFAULT_DOWNLOAD_PREFIX).expect("valid url"); let fetcher = ListVersionFetcher::new(download_url); let verions = fetcher.parse_json_versions(list_json_file).unwrap(); @@ -296,21 +342,21 @@ mod tests { "https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.4.13+commit.0fb4cb1a", ); assert_has_version(&verions, - "10.8.9-nightly.2021.9.11+commit.e5eed63a", - "https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v10.8.9-nightly.2021.9.11+commit.e5eed63a" + "10.8.9-nightly.2021.9.11+commit.e5eed63a", + "https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v10.8.9-nightly.2021.9.11+commit.e5eed63a" ); assert_has_version(&verions, - "0.4.16+commit.d7661dd9", - "https://binaries.soliditylang.org/linux-amd64/download/files/solc-linux-amd64-v0.4.16+commit.d7661dd9" + "0.4.16+commit.d7661dd9", + "https://binaries.soliditylang.org/linux-amd64/download/files/solc-linux-amd64-v0.4.16+commit.d7661dd9" ); } #[tokio::test] async fn list_download_versions() { let list_url = Url::try_from(DEFAULT_SOLIDITY_COMPILER_LIST).expect("valid url"); - let fetcher = ListFetcher::new( + let fetcher: ListFetcher = ListFetcher::new( list_url, - std::env::temp_dir().join("blockscout/smart_contract_verifier/compiler_fetcher/test/"), + temp_dir().join("blockscout/smart_contract_verifier/compiler_fetcher/test/"), None, None, ) @@ -318,8 +364,8 @@ mod tests { .expect("list.json file should be valid"); for compiler_version in [ - Version::from_str("0.7.0+commit.9e61f92b").unwrap(), - Version::from_str("0.8.9+commit.e5eed63a").unwrap(), + DetailedVersion::from_str("0.7.0+commit.9e61f92b").unwrap(), + DetailedVersion::from_str("0.8.9+commit.e5eed63a").unwrap(), ] { let file = fetcher.fetch(&compiler_version).await.unwrap(); let solc = Solc::new(file); @@ -345,7 +391,7 @@ mod tests { .respond_with(ResponseTemplate::new(200).set_body_bytes("{\"builds\": []}")) .mount(&mock_server) .await; - let fetcher = ListFetcher::new( + let fetcher: ListFetcher = ListFetcher::new( Url::parse(&mock_server.uri()).unwrap(), temp_dir(), Some(Schedule::from_str("* * * * * * *").unwrap()), @@ -366,7 +412,7 @@ mod tests { tokio::time::sleep(tokio::time::Duration::from_millis(1500)).await; let versions = fetcher.all_versions(); assert!( - versions.contains(&Version::from_str("0.4.13+commit.0fb4cb1a").unwrap()), + versions.contains(&DetailedVersion::from_str("0.4.13+commit.0fb4cb1a").unwrap()), "versions list doesn't have 0.4.13: {versions:?}", ); } @@ -395,7 +441,7 @@ mod tests { .respond_with(ResponseTemplate::new(200).set_body_bytes(VYPER_LIST_JSON)) .mount(&mock_server) .await; - let fetcher = ListFetcher::new( + let fetcher: ListFetcher = ListFetcher::new( Url::parse(&mock_server.uri()).unwrap(), temp_dir(), None, @@ -406,7 +452,7 @@ mod tests { let versions = fetcher.all_versions(); assert!( - versions.contains(&Version::from_str("0.3.2+commit.3b6a4117").unwrap()), + versions.contains(&DetailedVersion::from_str("0.3.2+commit.3b6a4117").unwrap()), "versions list doesn't have 0.3.2: {versions:?}", ); @@ -416,4 +462,52 @@ mod tests { }); } } + + const ZKSOLC_LIST_JSON: &str = r#"{ + "builds": [ + { + "path": "https://github.com/matter-labs/zksolc-bin/releases/download/v1.4.1/zksolc-macosx-arm64-v1.4.1", + "version": "1.4.1", + "longVersion": "1.4.1", + "md5": "8736f04aaaca31dd78bbbe0bdd5c2443", + "sha256": "f9991aa8c227d2ce35672cd33cf93370febce736b26beb047ede1919de12a3b8" + } + ] + }"#; + + /// That's will try to download the ZkSolc compiler from the list.json file. + /// It check's: + /// 1) an access to a download link + /// 2) Hash (mis)matching + #[tokio::test] + async fn download_zksolc_versions() { + let mock_server = MockServer::start().await; + + // mock list.json server response with empty list + Mock::given(method("GET")) + .and(path("/")) + .respond_with(ResponseTemplate::new(200).set_body_bytes(ZKSOLC_LIST_JSON)) + .mount(&mock_server) + .await; + let fetcher: ListFetcher = ListFetcher::new( + Url::parse(&mock_server.uri()).unwrap(), + temp_dir(), + None, + None, + ) + .await + .expect("cannot initialize fetcher"); + + let versions = fetcher.all_versions(); + assert!( + versions.contains(&CompactVersion::from_str("v1.4.1").unwrap()), + "versions list doesn't have v1.4.1: {versions:?}", + ); + + for compiler_version in versions { + fetcher.fetch(&compiler_version).await.unwrap_or_else(|_| { + panic!("fetcher: can't download zksolc compiler {compiler_version}") + }); + } + } } diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/s3_fetcher.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_s3.rs similarity index 85% rename from smart-contract-verifier/smart-contract-verifier/src/compiler/s3_fetcher.rs rename to smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_s3.rs index 99b5d85e8..1e24c9b42 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/s3_fetcher.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_s3.rs @@ -1,14 +1,13 @@ use super::{ - fetcher::{FetchError, Fetcher, FileValidator}, - version::Version, - versions_fetcher::{VersionsFetcher, VersionsRefresher}, + fetcher::{FetchError, Fetcher, FileValidator, Version}, + fetcher_versions::{VersionsFetcher, VersionsRefresher}, }; use async_trait::async_trait; use bytes::Bytes; use cron::Schedule; use primitive_types::H256; use s3::{request_trait::ResponseData, Bucket}; -use std::{collections::HashSet, path::PathBuf, str::FromStr, sync::Arc}; +use std::{collections::HashSet, marker::PhantomData, path::PathBuf, str::FromStr, sync::Arc}; use thiserror::Error; use tokio::task::JoinHandle; use tracing::{debug, instrument}; @@ -19,19 +18,23 @@ enum ListError { Fetch(s3::error::S3Error), } -struct S3VersionFetcher { +struct S3VersionFetcher { bucket: Arc, + _phantom_data: PhantomData, } -impl S3VersionFetcher { - fn new(bucket: Arc) -> S3VersionFetcher { - S3VersionFetcher { bucket } +impl S3VersionFetcher { + fn new(bucket: Arc) -> S3VersionFetcher { + S3VersionFetcher { + bucket, + _phantom_data: Default::default(), + } } } #[async_trait] -impl VersionsFetcher for S3VersionFetcher { - type Versions = HashSet; +impl VersionsFetcher for S3VersionFetcher { + type Versions = HashSet; type Error = ListError; fn len(vers: &Self::Versions) -> usize { @@ -46,11 +49,11 @@ impl VersionsFetcher for S3VersionFetcher { .await .map_err(ListError::Fetch)?; - let fetched_versions: HashSet = folders + let fetched_versions: HashSet = folders .into_iter() .filter_map(|x| x.common_prefixes) .flatten() - .filter_map(|v| Version::from_str(v.prefix.trim_end_matches('/')).ok()) + .filter_map(|v| Ver::from_str(v.prefix.trim_end_matches('/')).ok()) .collect(); debug!( "found version on bucket of len = {}", @@ -60,11 +63,11 @@ impl VersionsFetcher for S3VersionFetcher { } } -pub struct S3Fetcher { +pub struct S3Fetcher { bucket: Arc, folder: PathBuf, - versions: VersionsRefresher>, - validator: Option>, + versions: VersionsRefresher>, + validator: Option>>, } fn spawn_fetch_s3( @@ -88,13 +91,13 @@ fn status_code_error(name: &str, status_code: u16) -> FetchError { )) } -impl S3Fetcher { +impl S3Fetcher { pub async fn new( bucket: Arc, folder: PathBuf, refresh_schedule: Option, - validator: Option>, - ) -> anyhow::Result { + validator: Option>>, + ) -> anyhow::Result> { let fetcher = Arc::new(S3VersionFetcher::new(bucket.clone())); let versions = VersionsRefresher::new(fetcher, refresh_schedule).await?; Ok(S3Fetcher { @@ -106,11 +109,11 @@ impl S3Fetcher { } #[instrument(skip(self), level = "debug")] - async fn fetch_file(&self, ver: &Version) -> Result<(Bytes, H256), FetchError> { + async fn fetch_file(&self, ver: &Ver) -> Result<(Bytes, H256), FetchError> { { let versions = self.versions.read(); if !versions.contains(ver) { - return Err(FetchError::NotFound(ver.clone())); + return Err(FetchError::NotFound(ver.clone().to_string())); } } @@ -138,14 +141,16 @@ impl S3Fetcher { } #[async_trait] -impl Fetcher for S3Fetcher { - async fn fetch(&self, ver: &Version) -> Result { +impl Fetcher for S3Fetcher { + type Version = Ver; + + async fn fetch(&self, ver: &Self::Version) -> Result { let (data, hash) = self.fetch_file(ver).await?; super::fetcher::write_executable(data, hash, &self.folder, ver, self.validator.as_deref()) .await } - fn all_versions(&self) -> Vec { + fn all_versions(&self) -> Vec { let versions = self.versions.read(); versions.iter().cloned().collect() } @@ -153,7 +158,7 @@ impl Fetcher for S3Fetcher { #[cfg(test)] mod tests { - use super::*; + use super::{super::version_detailed as evm_version, *}; use pretty_assertions::assert_eq; use s3::{creds::Credentials, Region}; use serde::Serialize; @@ -258,8 +263,8 @@ mod tests { .await; let versions = vec![ - Version::from_str("v0.4.10+commit.f0d539ae").unwrap(), - Version::from_str("v0.4.11+commit.68ef5810").unwrap(), + evm_version::DetailedVersion::from_str("v0.4.10+commit.f0d539ae").unwrap(), + evm_version::DetailedVersion::from_str("v0.4.11+commit.68ef5810").unwrap(), ]; // create type directly to avoid extra work in constructor @@ -296,7 +301,7 @@ mod tests { "v0.5.1+commit.c8a2cb62", ] .into_iter() - .map(Version::from_str) + .map(evm_version::DetailedVersion::from_str) .map(|x| x.unwrap()) .collect(); @@ -322,7 +327,7 @@ mod tests { "v0.5.1+commit.c8a2cb62", ] .into_iter() - .map(Version::from_str) + .map(evm_version::DetailedVersion::from_str) .map(|x| x.unwrap()) .collect(); diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/versions_fetcher.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_versions.rs similarity index 100% rename from smart-contract-verifier/smart-contract-verifier/src/compiler/versions_fetcher.rs rename to smart-contract-verifier/smart-contract-verifier/src/compiler/fetcher_versions.rs diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/mod.rs index 632ea5cc3..3f2c780bf 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/mod.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/mod.rs @@ -1,15 +1,16 @@ -mod version; - -mod fetcher; -mod list_fetcher; -mod s3_fetcher; -mod versions_fetcher; - mod compilers; mod download_cache; +mod fetcher; +mod fetcher_list; +mod fetcher_s3; +mod fetcher_versions; +mod version_compact; +mod version_detailed; pub use compilers::{CompilerInput, Compilers, Error, EvmCompiler}; -pub use fetcher::{Fetcher, FileValidator}; -pub use list_fetcher::ListFetcher; -pub use s3_fetcher::S3Fetcher; -pub use version::Version; +pub use download_cache::DownloadCache; +pub use fetcher::{FetchError, Fetcher, FileValidator, Version}; +pub use fetcher_list::ListFetcher; +pub use fetcher_s3::S3Fetcher; +pub use version_compact::CompactVersion; +pub use version_detailed::DetailedVersion; diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/version_compact.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/version_compact.rs new file mode 100644 index 000000000..9dd3e425b --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/version_compact.rs @@ -0,0 +1,97 @@ +use super::fetcher::Version; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; + +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct CompactVersion(semver::Version); + +impl Display for CompactVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "v{}", self.0) + } +} + +impl FromStr for CompactVersion { + type Err = semver::Error; + + fn from_str(s: &str) -> Result { + Ok(Self(semver::Version::from_str(s.trim_start_matches('v'))?)) + } +} + +impl Version for CompactVersion { + fn to_semver(&self) -> &semver::Version { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, SeedableRng}; + + fn check_parsing(ver_str: &str) -> T + where + ::Err: std::fmt::Debug, + { + T::from_str(ver_str).unwrap() + } + + #[test] + fn parse() { + let ver = check_parsing::("v1.4.1"); + assert_eq!(ver.0, semver::Version::new(1, 4, 1)); + let ver = check_parsing::("1.4.1"); + assert_eq!(ver.0, semver::Version::new(1, 4, 1)); + check_parsing::("v0.0.0"); + check_parsing::("v123456789.987654321.0"); + check_parsing::("v1.2.3"); + check_parsing::("v3.2.1"); + } + + #[test] + fn display_version() { + for (initial, expected) in [("v1.4.1", "v1.4.1"), ("1.4.1", "v1.4.1")] { + let version = check_parsing::(initial); + assert_eq!(version.to_string(), expected,); + } + } + + #[test] + fn order_versions() { + let ver = check_parsing::; + + assert!(ver("v1.3.1") > ver("v1.3.0")); + assert!(ver("v1.3.0") > ver("v1.2.9")); + assert!(ver("v1.13.0") > ver("v1.2.9")); + assert!(ver("v1.13.0") > ver("v1.3.1")); + } + + fn test_shuffle_and_sort(sorted: Vec<&str>, times: usize) { + let sorted_versions: Vec = sorted + .iter() + .map(|s| CompactVersion::from_str(s).expect("invalid version")) + .collect(); + // check, that array is indeed sorted + assert!(sorted_versions.windows(2).all(|vals| vals[0] <= vals[1])); + let seed = thread_rng().gen(); + let mut r = StdRng::seed_from_u64(seed); + let mut shuffled_versions = sorted_versions; + for i in 0..times { + shuffled_versions.shuffle(&mut r); + shuffled_versions.sort(); + let shuffled: Vec = shuffled_versions.iter().map(|v| v.to_string()).collect(); + // we compare vec of strings, because in case of wrong order + // test will show unreadable error (it will print a lot of large structures) + assert_eq!(shuffled, sorted, "seed={}, i={}", seed, i); + } + } + + #[test] + fn sort_all_versions() { + test_shuffle_and_sort(vec!["v1.2.9", "v1.3.0", "v1.3.1", "v1.13.0"], 100); + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/compiler/version.rs b/smart-contract-verifier/smart-contract-verifier/src/compiler/version_detailed.rs similarity index 91% rename from smart-contract-verifier/smart-contract-verifier/src/compiler/version.rs rename to smart-contract-verifier/smart-contract-verifier/src/compiler/version_detailed.rs index 79ce2f489..8ed672400 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/compiler/version.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/compiler/version_detailed.rs @@ -1,3 +1,4 @@ +use super::fetcher::Version; use chrono::NaiveDate; use semver::{BuildMetadata, Prerelease}; use std::{cmp::Ordering, fmt::Display, str::FromStr}; @@ -108,39 +109,45 @@ impl Display for NightlyVersion { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Version { +pub enum DetailedVersion { Release(ReleaseVersion), Nightly(NightlyVersion), } -impl Version { +impl DetailedVersion { pub fn version(&self) -> &semver::Version { match self { - Version::Nightly(v) => &v.version, - Version::Release(v) => &v.version, + DetailedVersion::Nightly(v) => &v.version, + DetailedVersion::Release(v) => &v.version, } } pub fn is_release(&self) -> bool { - matches!(self, Version::Release(_)) + matches!(self, DetailedVersion::Release(_)) } pub fn date(&self) -> Option<&NaiveDate> { match self { - Version::Nightly(v) => Some(&v.date), - Version::Release(_) => None, + DetailedVersion::Nightly(v) => Some(&v.date), + DetailedVersion::Release(_) => None, } } pub fn commit(&self) -> &str { match self { - Version::Nightly(v) => &v.commit, - Version::Release(v) => &v.commit, + DetailedVersion::Nightly(v) => &v.commit, + DetailedVersion::Release(v) => &v.commit, } } } -impl FromStr for Version { +impl Version for DetailedVersion { + fn to_semver(&self) -> &semver::Version { + self.version() + } +} + +impl FromStr for DetailedVersion { type Err = ParseError; /// Parses compiler version @@ -155,16 +162,16 @@ impl FromStr for Version { } } -impl Display for Version { +impl Display for DetailedVersion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Version::Release(v) => v.fmt(f), - Version::Nightly(v) => v.fmt(f), + DetailedVersion::Release(v) => v.fmt(f), + DetailedVersion::Nightly(v) => v.fmt(f), } } } -impl Ord for Version { +impl Ord for DetailedVersion { fn cmp(&self, other: &Self) -> Ordering { ( self.version(), @@ -181,7 +188,7 @@ impl Ord for Version { } } -impl PartialOrd for Version { +impl PartialOrd for DetailedVersion { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -265,18 +272,20 @@ mod tests { #[test] fn parse_version() { assert_eq!( - check_parsing::("v0.8.9+commit.e5eed63a"), - Version::Release(ReleaseVersion::from_str("v0.8.9+commit.e5eed63a").unwrap()) + check_parsing::("v0.8.9+commit.e5eed63a"), + DetailedVersion::Release(ReleaseVersion::from_str("v0.8.9+commit.e5eed63a").unwrap()) ); assert_eq!( - check_parsing::("v0.8.9-nightly.2021.9.11+commit.e5eed63a"), - Version::Nightly( + check_parsing::("v0.8.9-nightly.2021.9.11+commit.e5eed63a"), + DetailedVersion::Nightly( NightlyVersion::from_str("v0.8.9-nightly.2021.9.11+commit.e5eed63a").unwrap() ) ); assert_eq!( - check_parsing::("0.1.0-beta.16+commit.5e4a94a"), - Version::Release(ReleaseVersion::from_str("0.1.0-beta.16+commit.5e4a94a").unwrap()) + check_parsing::("0.1.0-beta.16+commit.5e4a94a"), + DetailedVersion::Release( + ReleaseVersion::from_str("0.1.0-beta.16+commit.5e4a94a").unwrap() + ) ); } @@ -293,14 +302,14 @@ mod tests { "v0.1.0-beta.16+commit.5e4a94a", ), ] { - let version = check_parsing::(initial); + let version = check_parsing::(initial); assert_eq!(version.to_string(), expected,); } } #[test] fn order_versions() { - let ver = check_parsing::; + let ver = check_parsing::; // Release only assert!(ver("v0.8.10+commit.fc410830") > ver("v0.8.9+commit.e5eed63a")); @@ -337,9 +346,9 @@ mod tests { } fn test_shuffle_and_sort(sorted: Vec<&str>, times: usize) { - let sorted_versions: Vec = sorted + let sorted_versions: Vec = sorted .iter() - .map(|s| Version::from_str(s).expect("invalid version")) + .map(|s| DetailedVersion::from_str(s).expect("invalid version")) .collect(); // check, that array is indeed sorted assert!(sorted_versions.windows(2).all(|vals| vals[0] <= vals[1])); diff --git a/smart-contract-verifier/smart-contract-verifier/src/consts.rs b/smart-contract-verifier/smart-contract-verifier/src/consts.rs index f9d762ffc..ffa5464eb 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/consts.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/consts.rs @@ -13,3 +13,10 @@ pub const DEFAULT_VYPER_COMPILER_LIST: &str = "https://raw.githubusercontent.com/blockscout/solc-bin/main/vyper.macos.list.json"; pub const DEFAULT_SOURCIFY_HOST: &str = "https://sourcify.dev/server/"; + +#[cfg(target_os = "linux")] +pub const DEFAULT_ZKSOLC_COMPILER_LIST: &str = + "https://raw.githubusercontent.com/blockscout/solc-bin/main/zksolc.linux-amd64.list.json"; +#[cfg(target_os = "macos")] +pub const DEFAULT_ZKSOLC_COMPILER_LIST: &str = + "https://raw.githubusercontent.com/blockscout/solc-bin/main/zksolc.macosx-arm64.list.json"; diff --git a/smart-contract-verifier/smart-contract-verifier/src/lib.rs b/smart-contract-verifier/smart-contract-verifier/src/lib.rs index e9074187c..ebf602a1e 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/lib.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/lib.rs @@ -15,11 +15,13 @@ mod verifier; mod batch_verifier; #[cfg(test)] mod tests; +pub mod zksync; pub(crate) use blockscout_display_bytes::Bytes as DisplayBytes; pub use consts::{ DEFAULT_SOLIDITY_COMPILER_LIST, DEFAULT_SOURCIFY_HOST, DEFAULT_VYPER_COMPILER_LIST, + DEFAULT_ZKSOLC_COMPILER_LIST, }; pub use middleware::Middleware; @@ -27,7 +29,10 @@ pub use middleware::Middleware; pub use crate::sourcify::Error as SourcifyError; pub use batch_verifier::{BatchError, BatchMatch, BatchSuccess, BatchVerificationResult}; pub use common_types::{Contract, MatchType}; -pub use compiler::{Compilers, Fetcher, ListFetcher, S3Fetcher, Version}; +pub use compiler::{ + CompactVersion, Compilers, DetailedVersion, Fetcher, FileValidator, ListFetcher, S3Fetcher, + Version, +}; pub use verifier::{BytecodePart, Error as VerificationError}; pub use crate::sourcify::{SourcifyApiClient, Success as SourcifySuccess}; @@ -36,3 +41,11 @@ pub use solidity::{ Client as SolidityClient, SolcValidator, SolidityCompiler, Success as SoliditySuccess, }; pub use vyper::{Client as VyperClient, Success as VyperSuccess, VyperCompiler}; + +pub fn decode_hex(value: &str) -> Result, hex::FromHexError> { + if let Some(value) = value.strip_prefix("0x") { + hex::decode(value) + } else { + hex::decode(value) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/compiler.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/compiler.rs index d188f4777..222b83ec2 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/compiler.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/compiler.rs @@ -1,5 +1,5 @@ use super::solc_cli; -use crate::compiler::{self, EvmCompiler, Version}; +use crate::compiler::{self, DetailedVersion, EvmCompiler}; use ethers_solc::{error::SolcError, CompilerOutput, Solc}; use foundry_compilers::artifacts::output_selection::OutputSelection; use std::path::Path; @@ -24,7 +24,7 @@ impl compiler::CompilerInput for foundry_compilers::CompilerInput { self } - fn normalize_output_selection(&mut self, _version: &Version) { + fn normalize_output_selection(&mut self, _version: &DetailedVersion) { self.settings.output_selection = OutputSelection::complete_output_selection(); } } @@ -36,7 +36,7 @@ impl EvmCompiler for SolidityCompiler { async fn compile( &self, path: &Path, - ver: &Version, + ver: &DetailedVersion, input: &Self::CompilerInput, ) -> Result<(serde_json::Value, CompilerOutput), SolcError> { if ver.version() < &semver::Version::new(0, 4, 11) { diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/multi_part.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/multi_part.rs index 135719033..106e92fdb 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/multi_part.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/multi_part.rs @@ -1,6 +1,6 @@ use super::{client::Client, types::Success}; use crate::{ - compiler::Version, + compiler::DetailedVersion, verifier::{ContractVerifier, Error}, BatchError, BatchVerificationResult, Contract, }; @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; pub struct VerificationRequest { pub deployed_bytecode: Bytes, pub creation_bytecode: Option, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: MultiFileContent, @@ -110,7 +110,7 @@ pub async fn verify(client: Arc, request: VerificationRequest) -> Result /// have to iterate through all possible options. /// /// See "settings_metadata" (https://docs.soliditylang.org/en/v0.8.15/using-the-compiler.html?highlight=compiler%20input#input-description) -fn settings_metadata(compiler_version: &Version) -> Vec> { +fn settings_metadata(compiler_version: &DetailedVersion) -> Vec> { // Options are sorted by their probability of occurring const BYTECODE_HASHES: [BytecodeHash; 3] = [BytecodeHash::Ipfs, BytecodeHash::None, BytecodeHash::Bzzr1]; @@ -160,7 +160,7 @@ fn input_from_sources(sources: Sources) -> Vec { pub struct BatchVerificationRequest { pub contracts: Vec, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: MultiFileContent, } diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/solc_cli.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/solc_cli.rs index 9def6e99c..b1f2e4607 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/solc_cli.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/solc_cli.rs @@ -268,7 +268,7 @@ pub async fn compile_using_cli( #[cfg(test)] mod tests { use super::*; - use crate::compiler::{Fetcher, ListFetcher, Version}; + use crate::compiler::{DetailedVersion, Fetcher, ListFetcher}; use ethers_solc::Artifact; use foundry_compilers::artifacts::{Settings, Source}; use hex::ToHex; @@ -461,7 +461,7 @@ mod tests { } } - async fn get_solc(ver: &Version) -> PathBuf { + async fn get_solc(ver: &DetailedVersion) -> PathBuf { let mock_server = MockServer::start().await; Mock::given(method("GET")) .and(path("/")) @@ -489,7 +489,7 @@ mod tests { #[tokio::test] async fn compile() { for ver in &["v0.4.8+commit.60cc1668", "v0.4.10+commit.f0d539ae"] { - let version = Version::from_str(ver).expect("valid version"); + let version = DetailedVersion::from_str(ver).expect("valid version"); let solc = get_solc(&version).await; let input: CompilerInput = serde_json::from_str(DEFAULT_COMPILER_INPUT).unwrap(); diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/standard_json.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/standard_json.rs index 0e3037860..ba8965886 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/standard_json.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/standard_json.rs @@ -1,7 +1,7 @@ use super::{client::Client, types::Success}; use crate::{ batch_verifier::BatchError, - compiler::Version, + compiler::DetailedVersion, verifier::{ContractVerifier, Error}, BatchVerificationResult, Contract, }; @@ -12,7 +12,7 @@ use std::sync::Arc; pub struct VerificationRequest { pub deployed_bytecode: Bytes, pub creation_bytecode: Option, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: StandardJsonContent, @@ -53,7 +53,7 @@ pub async fn verify(client: Arc, request: VerificationRequest) -> Result pub struct BatchVerificationRequest { pub contracts: Vec, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: StandardJsonContent, } diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/types.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/types.rs index db9799ab7..7ed34c98b 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/types.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/types.rs @@ -11,7 +11,7 @@ use foundry_compilers::CompilerInput; pub struct Success { pub compiler_input: CompilerInput, pub compiler_output: CompilerOutput, - pub compiler_version: compiler::Version, + pub compiler_version: compiler::DetailedVersion, pub file_path: String, pub contract_name: String, pub abi: Option, diff --git a/smart-contract-verifier/smart-contract-verifier/src/solidity/validator.rs b/smart-contract-verifier/smart-contract-verifier/src/solidity/validator.rs index b484c7813..b7230e355 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/solidity/validator.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/solidity/validator.rs @@ -1,5 +1,5 @@ use crate::compiler::{FileValidator, Version}; -use anyhow::Context; +use anyhow::{Context, Error}; use async_trait::async_trait; use ethers_solc::Solc; use std::path::Path; @@ -8,17 +8,17 @@ use std::path::Path; pub struct SolcValidator {} #[async_trait] -impl FileValidator for SolcValidator { - async fn validate(&self, ver: &Version, path: &Path) -> Result<(), anyhow::Error> { +impl FileValidator for SolcValidator { + async fn validate(&self, ver: &Ver, path: &Path) -> Result<(), Error> { let solc = Solc::new(path); let solc_ver = solc.version().context("could not get compiler version")?; // ignore build and pre metadata let solc_ver = semver::Version::new(solc_ver.major, solc_ver.minor, solc_ver.patch); - if &solc_ver != ver.version() { + if &solc_ver != ver.to_semver() { Err(anyhow::anyhow!( "versions don't match: expected={}, got={}", - ver.version(), + ver.to_semver(), solc_ver )) } else { @@ -31,7 +31,7 @@ impl FileValidator for SolcValidator { mod tests { use super::*; use crate::{ - compiler::{Fetcher, ListFetcher}, + compiler::{DetailedVersion, Fetcher, ListFetcher}, consts::DEFAULT_SOLIDITY_COMPILER_LIST, }; use std::{ @@ -39,8 +39,8 @@ mod tests { }; use tokio::sync::OnceCell; - fn default_version() -> Version { - Version::from_str("v0.8.9+commit.e5eed63a").unwrap() + fn default_version() -> DetailedVersion { + DetailedVersion::from_str("v0.8.9+commit.e5eed63a").unwrap() } async fn fetch_compiler() -> PathBuf { @@ -72,7 +72,7 @@ mod tests { async fn wrong_version() { let compiler = fetch_compiler().await; let validator = SolcValidator::default(); - let other_ver = Version::from_str("v0.8.10+commit.e5eed63a").unwrap(); + let other_ver = DetailedVersion::from_str("v0.8.10+commit.e5eed63a").unwrap(); validator .validate(&other_ver, compiler.as_path()) .await diff --git a/smart-contract-verifier/smart-contract-verifier/src/verifier/compiler_input.rs b/smart-contract-verifier/smart-contract-verifier/src/verifier/compiler_input.rs new file mode 100644 index 000000000..e69de29bb diff --git a/smart-contract-verifier/smart-contract-verifier/src/verifier/contract_verifier.rs b/smart-contract-verifier/smart-contract-verifier/src/verifier/contract_verifier.rs index 00b239f7e..3253b06c6 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/verifier/contract_verifier.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/verifier/contract_verifier.rs @@ -23,7 +23,7 @@ pub enum Error { #[error("{0}")] Initialization(anyhow::Error), #[error("Compiler version not found: {0}")] - VersionNotFound(compiler::Version), + VersionNotFound(String), #[error("Compilation error: {0:?}")] Compilation(Vec), #[error("{0}")] @@ -54,7 +54,7 @@ impl From for Error { #[derive(Clone, Debug)] pub struct Success { pub compiler_output: CompilerOutput, - pub compiler_version: compiler::Version, + pub compiler_version: compiler::DetailedVersion, pub file_path: String, pub contract_name: String, pub abi: Option, @@ -69,7 +69,7 @@ pub struct Success { pub struct ContractVerifier<'a, C> { compilers: &'a Compilers, - compiler_version: &'a compiler::Version, + compiler_version: &'a compiler::DetailedVersion, verifier: Box< dyn base::Verifier< Input = ( @@ -86,7 +86,7 @@ pub struct ContractVerifier<'a, C> { impl<'a, C: EvmCompiler> ContractVerifier<'a, C> { pub fn new( compilers: &'a Compilers, - compiler_version: &'a compiler::Version, + compiler_version: &'a compiler::DetailedVersion, creation_tx_input: Option, deployed_bytecode: Bytes, chain_id: Option, diff --git a/smart-contract-verifier/smart-contract-verifier/src/vyper/compiler.rs b/smart-contract-verifier/smart-contract-verifier/src/vyper/compiler.rs index 576094417..675230217 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/vyper/compiler.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/vyper/compiler.rs @@ -1,5 +1,5 @@ use super::artifacts::CompilerInput; -use crate::compiler::{self, EvmCompiler, Version}; +use crate::compiler::{self, DetailedVersion, EvmCompiler}; use ethers_solc::{error::SolcError, CompilerOutput, Solc}; use foundry_compilers::artifacts::output_selection::OutputSelection; use std::path::Path; @@ -28,7 +28,7 @@ impl compiler::CompilerInput for CompilerInput { // as some of them may be interfaces, which should not be compiled. // Thus, we start specifying required outputs only for those files // that already exists in the provided output_selection. - fn normalize_output_selection(&mut self, version: &Version) { + fn normalize_output_selection(&mut self, version: &DetailedVersion) { // v0.3.10 was the latest release prior to v0.4.0 pre-releases if version.version() > &semver::Version::new(0, 3, 10) { let default_output_selection = vec![ @@ -53,7 +53,7 @@ impl EvmCompiler for VyperCompiler { async fn compile( &self, path: &Path, - _ver: &Version, + _ver: &DetailedVersion, input: &Self::CompilerInput, ) -> Result<(serde_json::Value, CompilerOutput), SolcError> { let raw = Solc::from(path).async_compile_output(input).await?; @@ -232,7 +232,7 @@ def getUserName() -> String[100]: let compilers = global_compilers().await; let input: CompilerInput = input_with_source(source_code.into()); let version = - compiler::Version::from_str("0.3.6+commit.4a2124d0").expect("Compiler version"); + compiler::DetailedVersion::from_str("0.3.6+commit.4a2124d0").expect("Compiler version"); let (_raw, result) = compilers .compile(&version, &input, None) @@ -250,8 +250,8 @@ def getUserName() -> String[100]: #[tokio::test] async fn compile_failed() { let compilers = global_compilers().await; - let version = - compiler::Version::from_str("v0.2.11+commit.5db35ef").expect("Compiler version"); + let version = compiler::DetailedVersion::from_str("v0.2.11+commit.5db35ef") + .expect("Compiler version"); for sources in [ BTreeMap::from_iter([("source.vy".into(), "some wrong vyper code".into())]), diff --git a/smart-contract-verifier/smart-contract-verifier/src/vyper/multi_part.rs b/smart-contract-verifier/smart-contract-verifier/src/vyper/multi_part.rs index 66aef52c6..8d29aa301 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/vyper/multi_part.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/vyper/multi_part.rs @@ -4,7 +4,7 @@ use super::{ types::Success, }; use crate::{ - compiler::Version, + compiler::DetailedVersion, verifier::{ContractVerifier, Error}, }; use bytes::Bytes; @@ -18,7 +18,7 @@ use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; pub struct VerificationRequest { pub deployed_bytecode: Bytes, pub creation_bytecode: Option, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: MultiFileContent, diff --git a/smart-contract-verifier/smart-contract-verifier/src/vyper/standard_json.rs b/smart-contract-verifier/smart-contract-verifier/src/vyper/standard_json.rs index 83e57405c..523aa88e1 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/vyper/standard_json.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/vyper/standard_json.rs @@ -1,6 +1,6 @@ use super::{artifacts::CompilerInput, client::Client, types::Success}; use crate::{ - compiler::Version, + compiler::DetailedVersion, verifier::{ContractVerifier, Error}, }; use bytes::Bytes; @@ -9,7 +9,7 @@ use std::sync::Arc; pub struct VerificationRequest { pub deployed_bytecode: Bytes, pub creation_bytecode: Option, - pub compiler_version: Version, + pub compiler_version: DetailedVersion, pub content: StandardJsonContent, diff --git a/smart-contract-verifier/smart-contract-verifier/src/vyper/types.rs b/smart-contract-verifier/smart-contract-verifier/src/vyper/types.rs index f7138fd46..6ad5188fa 100644 --- a/smart-contract-verifier/smart-contract-verifier/src/vyper/types.rs +++ b/smart-contract-verifier/smart-contract-verifier/src/vyper/types.rs @@ -11,7 +11,7 @@ use ethers_solc::CompilerOutput; pub struct Success { pub compiler_input: CompilerInput, pub compiler_output: CompilerOutput, - pub compiler_version: compiler::Version, + pub compiler_version: compiler::DetailedVersion, pub file_path: String, pub contract_name: String, pub abi: Option, diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/implementation.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/implementation.rs new file mode 100644 index 000000000..09dbed4bf --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/implementation.rs @@ -0,0 +1,462 @@ +use crate::{ + compiler::{CompactVersion, DetailedVersion, DownloadCache, FetchError, Fetcher}, + decode_hex, + zksync::zksolc_standard_json::{input, input::Input, output, output::contract::Contract}, +}; +use anyhow::Context; +use async_trait::async_trait; +use bytes::Bytes; +use foundry_compilers::error::SolcError; +use futures::TryFutureExt; +use nonempty::NonEmpty; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_json::Value; +use std::{ + collections::BTreeMap, + marker::PhantomData, + path::{Path, PathBuf}, + sync::Arc, +}; +use thiserror::Error; +use tokio::sync::Semaphore; +use verification_common::verifier_alliance::{ + CompilationArtifacts, CreationCodeArtifacts, Match, MatchBuilder, RuntimeCodeArtifacts, + ToCompilationArtifacts, ToCreationCodeArtifacts, ToRuntimeCodeArtifacts, +}; + +#[derive(Clone, Debug)] +pub struct VerificationRequest { + pub code: Bytes, + pub constructor_arguments: Option, + pub zk_compiler: CompactVersion, + pub solc_compiler: DetailedVersion, + pub content: Input, +} + +#[derive(Clone, Debug)] +pub struct VerificationSuccess { + pub file_path: String, + pub contract_name: String, + pub compilation_artifacts: CompilationArtifacts, + pub creation_code_artifacts: CreationCodeArtifacts, + pub runtime_code_artifacts: RuntimeCodeArtifacts, + pub creation_match: Option, + pub runtime_match: Match, +} + +#[derive(Clone, Debug)] +pub struct VerificationFailure { + pub file_path: String, + pub contract_name: String, + pub message: String, +} + +pub struct VerificationResult { + pub zk_compiler: String, + pub zk_compiler_version: CompactVersion, + pub evm_compiler: String, + pub evm_compiler_version: DetailedVersion, + pub language: String, + pub compiler_settings: Value, + pub sources: BTreeMap, + pub successes: Vec, + pub failures: Vec, +} + +// TODO: uses on assumption, that only full matches are detected. Should be +// updated, when zksync verifier starts to detect partial matches as well. +pub fn choose_best_success(successes: Vec) -> Option { + let mut best = None; + for success in successes { + if success.creation_match.is_some() { + best = Some(success); + break; + } else if best.is_none() { + best = Some(success) + } + } + + best +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("Zk compiler not found: {0}")] + ZkCompilerNotFound(String), + #[error("Evm compiler not found: {0}")] + EvmCompilerNotFound(String), + #[error("Compilation error: {0:?}")] + Compilation(Vec), + #[error("{0:#?}")] + Internal(#[from] anyhow::Error), +} + +pub async fn verify( + compilers: &ZkSyncCompilers, + request: VerificationRequest, +) -> Result { + let zk_compiler_version = request.zk_compiler; + let evm_compiler_version = request.solc_compiler; + let mut compiler_input = request.content; + + compiler_input.normalize_output_selection(&zk_compiler_version); + + let (compiler_output, _raw_compiler_output) = compilers + .compile(&zk_compiler_version, &evm_compiler_version, &compiler_input) + .await?; + + let mut successes = Vec::new(); + let mut failures = Vec::new(); + for (file, contracts) in compiler_output.contracts.unwrap_or_default() { + for (name, contract) in contracts { + match check_contract( + file.clone(), + name, + request.code.clone(), + request.constructor_arguments.clone(), + contract, + )? { + Ok(success) => { + tracing::trace!( + file = success.file_path, + contract = success.contract_name, + "contract matches; creation_match={:?}, runtime_match={}", + success.creation_match.as_ref().map(|v| &v.r#type), + success.runtime_match.r#type + ); + successes.push(success); + } + Err(failure) => { + tracing::trace!( + file = failure.file_path, + contract = failure.contract_name, + "contract does not match; error={}", + failure.message + ); + failures.push(failure); + } + } + } + } + + let sources = compiler_input + .sources + .into_iter() + .map(|(file, source)| (file, source.content.to_string())) + .collect(); + Ok(VerificationResult { + zk_compiler: "zksolc".to_string(), + zk_compiler_version, + evm_compiler: "solc".to_string(), + evm_compiler_version, + language: "solidity".to_string(), + compiler_settings: serde_json::to_value(compiler_input.settings) + .context("compiler settings serialization")?, + sources, + successes, + failures, + }) +} + +fn check_contract( + file_path: String, + contract_name: String, + code: Bytes, + constructor_arguments: Option, + contract: Contract, +) -> Result, anyhow::Error> { + let failure = |message: String| { + Ok(Err(VerificationFailure { + file_path: file_path.clone(), + contract_name: contract_name.clone(), + message, + })) + }; + + if let Some(bytecode) = contract.evm.as_ref().and_then(|evm| evm.bytecode.as_ref()) { + let compiled_code = decode_hex(&bytecode.object); + if let Ok(compiled_code) = compiled_code { + let compilation_artifacts = CompilationArtifacts::from(&contract); + let creation_code_artifacts = CreationCodeArtifacts::from(&contract); + let runtime_code_artifacts = RuntimeCodeArtifacts::from(&contract); + + let runtime_match = build_runtime_match( + code.as_ref(), + compiled_code.clone(), + &runtime_code_artifacts, + ) + .context("runtime match")?; + if let Some(runtime_match) = runtime_match { + let mut creation_code = code.to_vec(); + creation_code.extend(constructor_arguments.unwrap_or_default()); + let creation_match = build_creation_match( + &creation_code, + compiled_code, + &creation_code_artifacts, + &compilation_artifacts, + ) + .context("creation match")?; + Ok(Ok(VerificationSuccess { + file_path, + contract_name, + compilation_artifacts, + creation_code_artifacts, + runtime_code_artifacts, + creation_match, + runtime_match, + })) + } else { + failure("compiled bytecode does not match the deployed one".into()) + } + } else { + failure(format!( + "compiled bytecode.object is not a valid hex; object={}; err={}", + bytecode.object, + compiled_code.unwrap_err() + )) + } + } else { + failure("compiled bytecode is null".into()) + } +} + +fn build_runtime_match( + code: &[u8], + compiled_code: Vec, + runtime_code_artifacts: &RuntimeCodeArtifacts, +) -> Result, anyhow::Error> { + let runtime_match_builder = MatchBuilder::new(code, compiled_code.clone()); + let runtime_match = if let Some(runtime_match_builder) = runtime_match_builder { + runtime_match_builder + .set_has_cbor_auxdata(true) + .apply_runtime_code_transformations(runtime_code_artifacts) + .context("applying transformations")? + .verify_and_build() + } else { + None + }; + Ok(runtime_match) +} + +fn build_creation_match( + code: &[u8], + compiled_code: Vec, + creation_code_artifacts: &CreationCodeArtifacts, + compilation_artifacts: &CompilationArtifacts, +) -> Result, anyhow::Error> { + let creation_match_builder = MatchBuilder::new(code, compiled_code); + let creation_match = if let Some(creation_match_builder) = creation_match_builder { + creation_match_builder + .set_has_cbor_auxdata(true) + .apply_creation_code_transformations(creation_code_artifacts, compilation_artifacts) + .context("applying transformations")? + .verify_and_build() + } else { + None + }; + Ok(creation_match) +} + +pub trait CompilerInput { + /// Modifies input so that the corresponding bytecode + /// should have modified metadata hash, if any. + fn modify(self) -> Self; + + fn normalize_output_selection(&mut self, version: &CompactVersion); +} + +impl CompilerInput for Input { + fn modify(mut self) -> Self { + // TODO: could we update some other field to avoid copying strings? + self.sources.iter_mut().for_each(|(_file, source)| { + let mut modified_content = source.content.as_ref().clone(); + modified_content.push(' '); + source.content = Arc::new(modified_content); + }); + self + } + + fn normalize_output_selection(&mut self, _version: &CompactVersion) { + use input::settings::selection::{ + file::{flag::Flag, File}, + Selection, + }; + let output_selection = Selection { + all: Some(File { + per_file: None, + per_contract: Some( + [Flag::ABI, Flag::Devdoc, Flag::Userdoc, Flag::StorageLayout].into(), + ), + }), + }; + self.settings.output_selection = Some(output_selection); + } +} + +pub trait CompilerOutput { + fn check_errors(&self) -> Option>; +} + +impl CompilerOutput for output::Output { + fn check_errors(&self) -> Option> { + // Compilations errors, warnings and info messages are returned in `CompilerOutput.errors` + let mut errors = Vec::new(); + for err in self.errors.clone().unwrap_or_default() { + if err.severity == "error" { + errors.push(err.formatted_message.clone()) + } + } + NonEmpty::from_vec(errors) + } +} + +impl ToCompilationArtifacts for Contract { + fn abi(&self) -> Option { + self.abi.clone() + } + + fn devdoc(&self) -> Option { + self.devdoc.clone() + } + + fn userdoc(&self) -> Option { + self.userdoc.clone() + } + fn storage_layout(&self) -> Option { + self.storage_layout.clone() + } +} + +impl ToCreationCodeArtifacts for Contract {} + +impl ToRuntimeCodeArtifacts for Contract {} + +#[async_trait] +pub trait ZkSyncCompiler { + type CompilerInput; + type CompilerOutput: CompilerOutput + DeserializeOwned; + + async fn compile( + zk_compiler_path: &Path, + evm_compiler_path: &Path, + input: &Self::CompilerInput, + ) -> Result; +} + +pub struct ZkSyncCompilers { + evm_cache: DownloadCache, + evm_fetcher: Arc>, + zk_cache: DownloadCache, + zk_fetcher: Arc>, + threads_semaphore: Arc, + _phantom_data: PhantomData, +} + +impl ZkSyncCompilers { + pub fn new( + evm_fetcher: Arc>, + zk_fetcher: Arc>, + threads_semaphore: Arc, + ) -> Self { + Self { + evm_cache: DownloadCache::default(), + evm_fetcher, + zk_cache: DownloadCache::default(), + zk_fetcher, + threads_semaphore, + _phantom_data: Default::default(), + } + } + + pub async fn compile( + &self, + zk_compiler: &CompactVersion, + evm_compiler: &DetailedVersion, + input: &ZkC::CompilerInput, + ) -> Result<(ZkC::CompilerOutput, Value), Error> { + let (zk_path, evm_path) = self.fetch_compilers(zk_compiler, evm_compiler).await?; + + let _permit = self + .threads_semaphore + .acquire() + .await + .context("acquiring lock")?; + + let raw_compiler_output = ZkC::compile(&zk_path, &evm_path, input) + .await + .context("compilation")?; + + let compiler_output = ZkC::CompilerOutput::deserialize(&raw_compiler_output) + .context("deserializing compiler output")?; + if let Some(errors) = compiler_output.check_errors() { + return Err(Error::Compilation(errors.into())); + } + + Ok((compiler_output, raw_compiler_output)) + } + + pub fn all_evm_versions_sorted_str(&self) -> Vec { + let mut versions = self.evm_fetcher.all_versions(); + // sort in descending order + versions.sort_by(|x, y| x.cmp(y).reverse()); + versions.into_iter().map(|v| v.to_string()).collect() + } + + pub fn all_zk_versions_sorted_str(&self) -> Vec { + let mut versions = self.zk_fetcher.all_versions(); + // sort in descending order + versions.sort_by(|x, y| x.cmp(y).reverse()); + versions.into_iter().map(|v| v.to_string()).collect() + } +} + +impl ZkSyncCompilers { + pub async fn fetch_compilers( + &self, + zk_compiler: &CompactVersion, + evm_compiler: &DetailedVersion, + ) -> Result<(PathBuf, PathBuf), Error> { + let zk_path_future = self + .zk_cache + .get(self.zk_fetcher.as_ref(), zk_compiler) + .map_err(|err| match err { + FetchError::NotFound(version) => Error::ZkCompilerNotFound(version), + err => anyhow::Error::new(err) + .context("fetching zk compiler") + .into(), + }); + + let evm_path_future = self + .evm_cache + .get(self.evm_fetcher.as_ref(), evm_compiler) + .map_err(|err| match err { + FetchError::NotFound(version) => Error::EvmCompilerNotFound(version), + err => anyhow::Error::new(err) + .context("fetching evm compiler") + .into(), + }); + + let (zk_path_result, evm_path_result) = futures::join!(zk_path_future, evm_path_future); + Ok((zk_path_result?, evm_path_result?)) + } +} + +#[derive(Default)] +pub struct ZkSolcCompiler {} + +#[async_trait] +impl ZkSyncCompiler for ZkSolcCompiler { + type CompilerInput = Input; + type CompilerOutput = output::Output; + + async fn compile( + zk_compiler_path: &Path, + evm_compiler_path: &Path, + input: &Self::CompilerInput, + ) -> Result { + foundry_compilers::Solc::new(zk_compiler_path) + .arg(format!("--solc={}", evm_compiler_path.to_string_lossy())) + .async_compile_as(input) + .await + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/mod.rs new file mode 100644 index 000000000..182b976d6 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/mod.rs @@ -0,0 +1,4 @@ +mod implementation; +pub mod zksolc_standard_json; + +pub use implementation::*; diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/eravm_metadata_hash.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/eravm_metadata_hash.rs new file mode 100644 index 000000000..378891484 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/eravm_metadata_hash.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] +pub enum MetadataHash { + /// Do not include bytecode hash. + #[serde(rename = "none")] + None, + /// The default keccak256 hash. + #[serde(rename = "keccak256")] + Keccak256, +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/mod.rs new file mode 100644 index 000000000..026016455 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/era_compiler_llvm_context/mod.rs @@ -0,0 +1,3 @@ +mod eravm_metadata_hash; + +pub use eravm_metadata_hash::MetadataHash as EraVMMetadataHash; diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/language.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/language.rs new file mode 100644 index 000000000..b5643a987 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/language.rs @@ -0,0 +1,25 @@ +//! +//! The `solc --standard-json` input language. +//! + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` input language. +/// +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Language { + /// The Solidity language. + Solidity, + /// The Yul IR. + Yul, +} + +impl std::fmt::Display for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Solidity => write!(f, "Solidity"), + Self::Yul => write!(f, "Yul"), + } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/mod.rs new file mode 100644 index 000000000..a4928171b --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/mod.rs @@ -0,0 +1,36 @@ +//! +//! The `solc --standard-json` input. +//! + +pub mod language; +pub mod settings; +pub mod source; + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +use self::{language::Language, settings::Settings, source::Source}; + +/// +/// The `solc --standard-json` input. +/// +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Input { + /// The input language. + pub language: Language, + /// The input source code files hashmap. + pub sources: BTreeMap, + /// The compiler settings. + pub settings: Settings, +} + +impl Input { + /// + /// Sets the necessary defaults. + /// + pub fn normalize(&mut self, version: &semver::Version) { + self.settings.normalize(version); + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/metadata.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/metadata.rs new file mode 100644 index 000000000..02bf0b8f7 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/metadata.rs @@ -0,0 +1,35 @@ +//! +//! The `solc --standard-json` input settings metadata. +//! + +use super::super::super::era_compiler_llvm_context; +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` input settings metadata. +/// +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Metadata { + /// The bytecode hash mode. + #[serde(skip_serializing_if = "Option::is_none")] + pub bytecode_hash: Option, + /// Whether to use literal content. + #[serde(skip_serializing_if = "Option::is_none")] + pub use_literal_content: Option, +} + +impl Metadata { + /// + /// A shortcut constructor. + /// + pub fn new( + bytecode_hash: era_compiler_llvm_context::EraVMMetadataHash, + use_literal_content: bool, + ) -> Self { + Self { + bytecode_hash: Some(bytecode_hash), + use_literal_content: Some(use_literal_content), + } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/mod.rs new file mode 100644 index 000000000..44d41c133 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/mod.rs @@ -0,0 +1,107 @@ +//! +//! The `solc --standard-json` input settings. +//! + +pub mod metadata; +pub mod optimizer; +pub mod selection; + +use std::collections::{BTreeMap, BTreeSet}; + +use serde::{Deserialize, Serialize}; + +use self::{metadata::Metadata, optimizer::Optimizer, selection::Selection}; + +/// +/// The `solc --standard-json` input settings. +/// +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Settings { + /// The target EVM version. + #[serde(skip_serializing_if = "Option::is_none")] + pub evm_version: Option, + /// The linker library addresses. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub libraries: Option>>, + /// The sorted list of remappings. + #[serde(skip_serializing_if = "Option::is_none")] + pub remappings: Option>, + /// The output selection filters. + #[serde(skip_serializing_if = "Option::is_none")] + pub output_selection: Option, + /// Whether to compile via IR. Only for testing with solc >=0.8.13. + #[serde( + rename = "viaIR", + skip_serializing_if = "Option::is_none", + skip_deserializing + )] + pub via_ir: Option, + /// The optimizer settings. + pub optimizer: Optimizer, + /// The metadata settings. + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +impl Settings { + /// + /// A shortcut constructor. + /// + pub fn new( + evm_version: Option, + libraries: BTreeMap>, + remappings: Option>, + output_selection: Selection, + via_ir: bool, + optimizer: Optimizer, + metadata: Option, + ) -> Self { + Self { + evm_version, + libraries: Some(libraries), + remappings, + output_selection: Some(output_selection), + via_ir: if via_ir { Some(true) } else { None }, + optimizer, + metadata, + } + } + + /// + /// Sets the necessary defaults. + /// + pub fn normalize(&mut self, version: &semver::Version) { + self.optimizer.normalize(version); + } + + /// + /// Parses the library list and returns their double hashmap with path and name as keys. + /// + pub fn parse_libraries( + input: Vec, + ) -> anyhow::Result>> { + let mut libraries = BTreeMap::new(); + for (index, library) in input.into_iter().enumerate() { + let mut path_and_address = library.split('='); + let path = path_and_address + .next() + .ok_or_else(|| anyhow::anyhow!("The library #{} path is missing", index))?; + let mut file_and_contract = path.split(':'); + let file = file_and_contract + .next() + .ok_or_else(|| anyhow::anyhow!("The library `{}` file name is missing", path))?; + let contract = file_and_contract.next().ok_or_else(|| { + anyhow::anyhow!("The library `{}` contract name is missing", path) + })?; + let address = path_and_address + .next() + .ok_or_else(|| anyhow::anyhow!("The library `{}` address is missing", path))?; + libraries + .entry(file.to_owned()) + .or_insert_with(BTreeMap::new) + .insert(contract.to_owned(), address.to_owned()); + } + Ok(libraries) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/details.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/details.rs new file mode 100644 index 000000000..bddeaf2dd --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/details.rs @@ -0,0 +1,66 @@ +//! +//! The `solc --standard-json` input settings optimizer details. +//! + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` input settings optimizer details. +/// +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Details { + /// Whether the pass is enabled. + pub peephole: bool, + /// Whether the pass is enabled. + #[serde(skip_serializing_if = "Option::is_none")] + pub inliner: Option, + /// Whether the pass is enabled. + pub jumpdest_remover: bool, + /// Whether the pass is enabled. + pub order_literals: bool, + /// Whether the pass is enabled. + pub deduplicate: bool, + /// Whether the pass is enabled. + pub cse: bool, + /// Whether the pass is enabled. + pub constant_optimizer: bool, +} + +impl Details { + /// + /// A shortcut constructor. + /// + pub fn new( + peephole: bool, + inliner: Option, + jumpdest_remover: bool, + order_literals: bool, + deduplicate: bool, + cse: bool, + constant_optimizer: bool, + ) -> Self { + Self { + peephole, + inliner, + jumpdest_remover, + order_literals, + deduplicate, + cse, + constant_optimizer, + } + } + + /// + /// Creates a set of disabled optimizations. + /// + pub fn disabled(version: &semver::Version) -> Self { + let inliner = if version >= &semver::Version::new(0, 8, 5) { + Some(false) + } else { + None + }; + + Self::new(false, inliner, false, false, false, false, false) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/mod.rs new file mode 100644 index 000000000..820e05f15 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/optimizer/mod.rs @@ -0,0 +1,68 @@ +//! +//! The `solc --standard-json` input settings optimizer. +//! + +pub mod details; + +use serde::{Deserialize, Serialize}; + +use self::details::Details; + +/// +/// The `solc --standard-json` input settings optimizer. +/// +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Optimizer { + /// Whether the optimizer is enabled. + pub enabled: bool, + /// The optimization mode string. + #[serde(skip_serializing_if = "Option::is_none")] + pub mode: Option, + /// The `solc` optimizer details. + #[serde(skip_serializing_if = "Option::is_none")] + pub details: Option
, + /// Whether to try to recompile with -Oz if the bytecode is too large. + #[serde(skip_serializing_if = "Option::is_none")] + pub fallback_to_optimizing_for_size: Option, + /// Whether to disable the system request memoization. + #[serde(skip_serializing_if = "Option::is_none")] + pub disable_system_request_memoization: Option, + /// Set the jump table density threshold. + #[serde(skip_serializing_if = "Option::is_none")] + pub jump_table_density_threshold: Option, +} + +impl Optimizer { + /// + /// A shortcut constructor. + /// + pub fn new( + enabled: bool, + mode: Option, + version: &semver::Version, + fallback_to_optimizing_for_size: bool, + disable_system_request_memoization: bool, + jump_table_density_threshold: Option, + ) -> Self { + Self { + enabled, + mode, + details: Some(Details::disabled(version)), + fallback_to_optimizing_for_size: Some(fallback_to_optimizing_for_size), + disable_system_request_memoization: Some(disable_system_request_memoization), + jump_table_density_threshold, + } + } + + /// + /// Sets the necessary defaults. + /// + pub fn normalize(&mut self, version: &semver::Version) { + self.details = if version >= &semver::Version::new(0, 5, 5) { + Some(Details::disabled(version)) + } else { + None + }; + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/flag.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/flag.rs new file mode 100644 index 000000000..1c7e8ec53 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/flag.rs @@ -0,0 +1,55 @@ +//! +//! The `solc --standard-json` expected output selection flag. +//! + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` expected output selection flag. +/// +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Flag { + /// The ABI JSON. + #[serde(rename = "abi")] + ABI, + /// The metadata. + #[serde(rename = "metadata")] + Metadata, + /// The developer documentation. + #[serde(rename = "devdoc")] + Devdoc, + /// The user documentation. + #[serde(rename = "userdoc")] + Userdoc, + /// The function signature hashes JSON. + #[serde(rename = "evm.methodIdentifiers")] + MethodIdentifiers, + /// The storage layout. + #[serde(rename = "storageLayout")] + StorageLayout, + /// The AST JSON. + #[serde(rename = "ast")] + AST, + /// The Yul IR. + #[serde(rename = "irOptimized")] + Yul, + /// The EVM legacy assembly JSON. + #[serde(rename = "evm.legacyAssembly")] + EVMLA, +} + +impl std::fmt::Display for Flag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ABI => write!(f, "abi"), + Self::Metadata => write!(f, "metadata"), + Self::Devdoc => write!(f, "devdoc"), + Self::Userdoc => write!(f, "userdoc"), + Self::MethodIdentifiers => write!(f, "evm.methodIdentifiers"), + Self::StorageLayout => write!(f, "storageLayout"), + Self::AST => write!(f, "ast"), + Self::Yul => write!(f, "irOptimized"), + Self::EVMLA => write!(f, "evm.legacyAssembly"), + } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/mod.rs new file mode 100644 index 000000000..ac44cac23 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/file/mod.rs @@ -0,0 +1,24 @@ +//! +//! The `solc --standard-json` output file selection. +//! + +pub mod flag; + +use std::collections::HashSet; + +use serde::{Deserialize, Serialize}; + +use self::flag::Flag as SelectionFlag; + +/// +/// The `solc --standard-json` output file selection. +/// +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct File { + /// The per-file output selections. + #[serde(rename = "", skip_serializing_if = "Option::is_none")] + pub per_file: Option>, + /// The per-contract output selections. + #[serde(rename = "*", skip_serializing_if = "Option::is_none")] + pub per_contract: Option>, +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/mod.rs new file mode 100644 index 000000000..504074dfe --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/settings/selection/mod.rs @@ -0,0 +1,19 @@ +//! +//! The `solc --standard-json` output selection. +//! + +pub mod file; + +use serde::{Deserialize, Serialize}; + +use self::file::File as FileSelection; + +/// +/// The `solc --standard-json` output selection. +/// +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Selection { + /// Only the 'all' wildcard is available for robustness reasons. + #[serde(rename = "*", skip_serializing_if = "Option::is_none")] + pub all: Option, +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/source.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/source.rs new file mode 100644 index 000000000..7177b95d1 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/input/source.rs @@ -0,0 +1,46 @@ +//! +//! The `solc --standard-json` input source. +//! + +use std::{io::Read, path::Path, sync::Arc}; + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` input source. +/// +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Source { + /// The source code file content. + pub content: Arc, +} + +impl From for Source { + fn from(content: String) -> Self { + Self { + content: Arc::new(content), + } + } +} + +impl TryFrom<&Path> for Source { + type Error = anyhow::Error; + + fn try_from(path: &Path) -> Result { + let content = if path.to_string_lossy() == "-" { + let mut solidity_code = String::with_capacity(16384); + std::io::stdin() + .read_to_string(&mut solidity_code) + .map_err(|error| anyhow::anyhow!(" reading error: {}", error))?; + solidity_code + } else { + std::fs::read_to_string(path) + .map_err(|error| anyhow::anyhow!("File {:?} reading error: {}", path, error))? + }; + + Ok(Self { + content: Arc::new(content), + }) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/mod.rs new file mode 100644 index 000000000..3acf02ef1 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/mod.rs @@ -0,0 +1,6 @@ +// Adapted from https://github.com/matter-labs/era-compiler-solidity/tree/1.4.1/src/solc/standard_json + +mod era_compiler_llvm_context; + +pub mod input; +pub mod output; diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/bytecode.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/bytecode.rs new file mode 100644 index 000000000..ddc118c2e --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/bytecode.rs @@ -0,0 +1,24 @@ +//! +//! The `solc --standard-json` output contract EVM bytecode. +//! + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` output contract EVM bytecode. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Bytecode { + /// The bytecode object. + pub object: String, +} + +impl Bytecode { + /// + /// A shortcut constructor. + /// + pub fn new(object: String) -> Self { + Self { object } + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/mod.rs new file mode 100644 index 000000000..023dc19a2 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/evm/mod.rs @@ -0,0 +1,27 @@ +//! +//! The `solc --standard-json` output contract EVM data. +//! + +pub mod bytecode; + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +use self::bytecode::Bytecode; + +/// +/// The `solc --standard-json` output contract EVM data. +/// +/// It is replaced by EraVM data after compiling. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct EVM { + /// The contract bytecode. + /// Is reset by that of EraVM before yielding the compiled project artifacts. + pub bytecode: Option, + /// The contract function signatures. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub method_identifiers: Option>, +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/mod.rs new file mode 100644 index 000000000..009d99b37 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/contract/mod.rs @@ -0,0 +1,49 @@ +//! +//! The `solc --standard-json` output contract. +//! + +pub mod evm; + +use std::collections::{BTreeMap, HashSet}; + +use serde::{Deserialize, Serialize}; + +use self::evm::EVM; + +/// +/// The `solc --standard-json` output contract. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Contract { + /// The contract ABI. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub abi: Option, + /// The contract metadata. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metadata: Option, + /// The contract developer documentation. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub devdoc: Option, + /// The contract user documentation. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub userdoc: Option, + /// The contract storage layout. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage_layout: Option, + /// Contract's bytecode and related objects + #[serde(default, skip_serializing_if = "Option::is_none")] + pub evm: Option, + /// The contract optimized IR code. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ir_optimized: Option, + /// The contract EraVM bytecode hash. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hash: Option, + /// The contract factory dependencies. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub factory_dependencies: Option>, + /// The contract missing libraries. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub missing_libraries: Option>, +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/mod.rs new file mode 100644 index 000000000..53947005c --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/mod.rs @@ -0,0 +1,176 @@ +//! +//! The `solc --standard-json` output error. +//! + +pub mod source_location; + +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +use self::source_location::SourceLocation; + +/// +/// The `solc --standard-json` output error. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Error { + /// The component type. + pub component: String, + /// The error code. + pub error_code: Option, + /// The formatted error message. + pub formatted_message: String, + /// The non-formatted error message. + pub message: String, + /// The error severity. + pub severity: String, + /// The error location data. + pub source_location: Option, + /// The error type. + pub r#type: String, +} + +impl Error { + /// + /// Returns the `ecrecover` function usage warning. + /// + pub fn message_ecrecover(src: Option<&str>) -> Self { + let message = r#" +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Warning: It looks like you are using 'ecrecover' to validate a signature of a user account. │ +│ zkSync Era comes with native account abstraction support, therefore it is highly recommended NOT │ +│ to rely on the fact that the account has an ECDSA private key attached to it since accounts might│ +│ implement other signature schemes. │ +│ Read more about Account Abstraction at https://v2-docs.zksync.io/dev/developer-guides/aa.html │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘"# + .to_owned(); + + Self { + component: "general".to_owned(), + error_code: None, + formatted_message: message.clone(), + message, + severity: "warning".to_owned(), + source_location: src.map(SourceLocation::from_str).and_then(Result::ok), + r#type: "Warning".to_owned(), + } + } + + /// + /// Returns the `
`'s `send` and `transfer` methods usage error. + /// + pub fn message_send_and_transfer(src: Option<&str>) -> Self { + let message = r#" +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Warning: It looks like you are using '
.send/transfer()' without providing │ +│ the gas amount. Such calls will fail depending on the pubdata costs. │ +│ This might be a false positive if you are using an interface (like IERC20) instead of the │ +│ native Solidity `send/transfer`. │ +│ Please use 'payable(
).call{value: }("")' instead, but be careful with the reentrancy │ +│ attack. `send` and `transfer` send limited amount of gas that prevents reentrancy, whereas │ +│ `
.call{value: }` sends all gas to the callee. Learn more on │ +│ https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘"# + .to_owned(); + + Self { + component: "general".to_owned(), + error_code: None, + formatted_message: message.clone(), + message, + severity: "warning".to_owned(), + source_location: src.map(SourceLocation::from_str).and_then(Result::ok), + r#type: "Warning".to_owned(), + } + } + + /// + /// Returns the `extcodesize` instruction usage warning. + /// + pub fn message_extcodesize(src: Option<&str>) -> Self { + let message = r#" +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is │ +│ usually needed in the following cases: │ +│ 1. To detect whether an address belongs to a smart contract. │ +│ 2. To detect whether the deploy code execution has finished. │ +│ zkSync Era comes with native account abstraction support (so accounts are smart contracts, │ +│ including private-key controlled EOAs), and you should avoid differentiating between contracts │ +│ and non-contract addresses. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘"# + .to_owned(); + + Self { + component: "general".to_owned(), + error_code: None, + formatted_message: message.clone(), + message, + severity: "warning".to_owned(), + source_location: src.map(SourceLocation::from_str).and_then(Result::ok), + r#type: "Warning".to_owned(), + } + } + + /// + /// Returns the `origin` instruction usage warning. + /// + pub fn message_tx_origin(src: Option<&str>) -> Self { + let message = r#" +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Warning: You are checking for 'tx.origin' in your code, which might lead to unexpected behavior. │ +│ zkSync Era comes with native account abstraction support, and therefore the initiator of a │ +│ transaction might be different from the contract calling your code. It is highly recommended NOT │ +│ to rely on tx.origin, but use msg.sender instead. │ +│ Read more about Account Abstraction at https://v2-docs.zksync.io/dev/developer-guides/aa.html │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘"# + .to_owned(); + + Self { + component: "general".to_owned(), + error_code: None, + formatted_message: message.clone(), + message, + severity: "warning".to_owned(), + source_location: src.map(SourceLocation::from_str).and_then(Result::ok), + r#type: "Warning".to_owned(), + } + } + + /// + /// Returns the internal function pointer usage error. + /// + pub fn message_internal_function_pointer(src: Option<&str>) -> Self { + let message = r#" +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Error: Internal function pointers are not supported in EVM legacy assembly pipeline. │ +│ Please use the Yul IR codegen instead. │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘"# + .to_owned(); + + Self { + component: "general".to_owned(), + error_code: None, + formatted_message: message.clone(), + message, + severity: "error".to_owned(), + source_location: src.map(SourceLocation::from_str).and_then(Result::ok), + r#type: "Error".to_owned(), + } + } + + /// + /// Appends the contract path to the message.. + /// + pub fn push_contract_path(&mut self, path: &str) { + self.formatted_message + .push_str(format!("\n--> {path}\n").as_str()); + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.formatted_message) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/source_location.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/source_location.rs new file mode 100644 index 000000000..0bd20874d --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/error/source_location.rs @@ -0,0 +1,46 @@ +//! +//! The `solc --standard-json` output error source location. +//! + +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` output error source location. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SourceLocation { + /// The source file path. + pub file: String, + /// The start location. + pub start: isize, + /// The end location. + pub end: isize, +} + +impl FromStr for SourceLocation { + type Err = anyhow::Error; + + fn from_str(string: &str) -> Result { + let mut parts = string.split(':'); + let start = parts + .next() + .map(|string| string.parse::()) + .and_then(Result::ok) + .unwrap_or_default(); + let length = parts + .next() + .map(|string| string.parse::()) + .and_then(Result::ok) + .unwrap_or_default(); + let file = parts.next().unwrap_or_default().to_owned(); + + Ok(Self { + file, + start, + end: start + length, + }) + } +} diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/mod.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/mod.rs new file mode 100644 index 000000000..5b622a75e --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/mod.rs @@ -0,0 +1,282 @@ +//! +//! The `solc --standard-json` output. +//! + +pub mod contract; +pub mod error; +pub mod source; + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; +// use sha3::Digest; +// +// use crate::evmla::assembly::instruction::Instruction; +// use crate::evmla::assembly::Assembly; +// use crate::project::contract::ir::IR as ProjectContractIR; +// use crate::project::contract::Contract as ProjectContract; +// use crate::project::Project; +// use crate::solc::pipeline::Pipeline as SolcPipeline; +// use crate::solc::version::Version as SolcVersion; +// use crate::warning::Warning; +// use crate::yul::lexer::Lexer; +// use crate::yul::parser::statement::object::Object; +// +use self::{contract::Contract, error::Error as SolcStandardJsonOutputError, source::Source}; + +/// +/// The `solc --standard-json` output. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Output { + /// The file-contract hashmap. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub contracts: Option>>, + /// The source code mapping data. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sources: Option>, + /// The compilation errors and warnings. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub errors: Option>, + /// The `solc` compiler version. + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, + /// The `solc` compiler long version. + #[serde(skip_serializing_if = "Option::is_none")] + pub long_version: Option, + /// The `zksolc` compiler version. + #[serde(skip_serializing_if = "Option::is_none")] + pub zk_version: Option, +} +// +// impl Output { +// /// +// /// Converts the `solc` JSON output into a convenient project. +// /// +// pub fn try_to_project( +// &mut self, +// source_code_files: BTreeMap, +// libraries: BTreeMap>, +// pipeline: SolcPipeline, +// solc_version: &SolcVersion, +// debug_config: Option<&era_compiler_llvm_context::DebugConfig>, +// ) -> anyhow::Result { +// if let SolcPipeline::EVMLA = pipeline { +// self.preprocess_dependencies()?; +// } +// +// let files = match self.contracts.as_ref() { +// Some(files) => files, +// None => { +// anyhow::bail!( +// "{}", +// self.errors +// .as_ref() +// .map(|errors| serde_json::to_string_pretty(errors).expect("Always valid")) +// .unwrap_or_else(|| "Unknown project assembling error".to_owned()) +// ); +// } +// }; +// let mut project_contracts = BTreeMap::new(); +// +// for (path, contracts) in files.iter() { +// for (name, contract) in contracts.iter() { +// let full_path = format!("{path}:{name}"); +// +// let source = match pipeline { +// SolcPipeline::Yul => { +// let ir_optimized = match contract.ir_optimized.to_owned() { +// Some(ir_optimized) => ir_optimized, +// None => continue, +// }; +// if ir_optimized.is_empty() { +// continue; +// } +// +// if let Some(debug_config) = debug_config { +// debug_config.dump_yul( +// full_path.as_str(), +// None, +// ir_optimized.as_str(), +// )?; +// } +// +// let mut lexer = Lexer::new(ir_optimized.to_owned()); +// let object = Object::parse(&mut lexer, None).map_err(|error| { +// anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error) +// })?; +// +// ProjectContractIR::new_yul(ir_optimized.to_owned(), object) +// } +// SolcPipeline::EVMLA => { +// let evm = contract.evm.as_ref(); +// let assembly = match evm.and_then(|evm| evm.assembly.to_owned()) { +// Some(assembly) => assembly.to_owned(), +// None => continue, +// }; +// let extra_metadata = evm +// .and_then(|evm| evm.extra_metadata.to_owned()) +// .unwrap_or_default(); +// +// ProjectContractIR::new_evmla(assembly, extra_metadata) +// } +// }; +// +// let source_code = source_code_files +// .get(path.as_str()) +// .ok_or_else(|| anyhow::anyhow!("Source code for path `{}` not found", path))?; +// let source_hash = sha3::Keccak256::digest(source_code.as_bytes()).into(); +// +// let project_contract = ProjectContract::new( +// full_path.clone(), +// source_hash, +// solc_version.to_owned(), +// source, +// contract.metadata.to_owned(), +// ); +// project_contracts.insert(full_path, project_contract); +// } +// } +// +// Ok(Project::new( +// solc_version.to_owned(), +// project_contracts, +// libraries, +// )) +// } +// +// /// +// /// Removes EVM artifacts to prevent their accidental usage. +// /// +// pub fn remove_evm(&mut self) { +// if let Some(files) = self.contracts.as_mut() { +// for (_, file) in files.iter_mut() { +// for (_, contract) in file.iter_mut() { +// if let Some(evm) = contract.evm.as_mut() { +// evm.bytecode = None; +// } +// } +// } +// } +// } +// +// /// +// /// Traverses the AST and returns the list of additional errors and warnings. +// /// +// pub fn preprocess_ast( +// &mut self, +// version: &SolcVersion, +// pipeline: SolcPipeline, +// suppressed_warnings: &[Warning], +// ) -> anyhow::Result<()> { +// let sources = match self.sources.as_ref() { +// Some(sources) => sources, +// None => return Ok(()), +// }; +// +// let mut messages = Vec::new(); +// for (path, source) in sources.iter() { +// if let Some(ast) = source.ast.as_ref() { +// let mut eravm_messages = +// Source::get_messages(ast, version, pipeline, suppressed_warnings); +// for message in eravm_messages.iter_mut() { +// message.push_contract_path(path.as_str()); +// } +// messages.extend(eravm_messages); +// } +// } +// +// self.errors = match self.errors.take() { +// Some(mut errors) => { +// errors.extend(messages); +// Some(errors) +// } +// None => Some(messages), +// }; +// +// Ok(()) +// } +// +// /// +// /// The pass, which replaces with dependency indexes with actual data. +// /// +// fn preprocess_dependencies(&mut self) -> anyhow::Result<()> { +// let files = match self.contracts.as_mut() { +// Some(files) => files, +// None => return Ok(()), +// }; +// let mut hash_path_mapping = BTreeMap::new(); +// +// for (path, contracts) in files.iter() { +// for (name, contract) in contracts.iter() { +// let full_path = format!("{path}:{name}"); +// let hash = match contract +// .evm +// .as_ref() +// .and_then(|evm| evm.assembly.as_ref()) +// .map(|assembly| assembly.keccak256()) +// { +// Some(hash) => hash, +// None => continue, +// }; +// +// hash_path_mapping.insert(hash, full_path); +// } +// } +// +// for (path, contracts) in files.iter_mut() { +// for (name, contract) in contracts.iter_mut() { +// let assembly = match contract.evm.as_mut().and_then(|evm| evm.assembly.as_mut()) { +// Some(assembly) => assembly, +// None => continue, +// }; +// +// let full_path = format!("{path}:{name}"); +// Self::preprocess_dependency_level( +// full_path.as_str(), +// assembly, +// &hash_path_mapping, +// )?; +// } +// } +// +// Ok(()) +// } +// +// /// +// /// Preprocesses an assembly JSON structure dependency data map. +// /// +// fn preprocess_dependency_level( +// full_path: &str, +// assembly: &mut Assembly, +// hash_path_mapping: &BTreeMap, +// ) -> anyhow::Result<()> { +// assembly.set_full_path(full_path.to_owned()); +// +// let deploy_code_index_path_mapping = +// assembly.deploy_dependencies_pass(full_path, hash_path_mapping)?; +// if let Some(deploy_code_instructions) = assembly.code.as_deref_mut() { +// Instruction::replace_data_aliases( +// deploy_code_instructions, +// &deploy_code_index_path_mapping, +// )?; +// }; +// +// let runtime_code_index_path_mapping = +// assembly.runtime_dependencies_pass(full_path, hash_path_mapping)?; +// if let Some(runtime_code_instructions) = assembly +// .data +// .as_mut() +// .and_then(|data_map| data_map.get_mut("0")) +// .and_then(|data| data.get_assembly_mut()) +// .and_then(|assembly| assembly.code.as_deref_mut()) +// { +// Instruction::replace_data_aliases( +// runtime_code_instructions, +// &runtime_code_index_path_mapping, +// )?; +// } +// +// Ok(()) +// } +// } diff --git a/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/source.rs b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/source.rs new file mode 100644 index 000000000..871f8dc20 --- /dev/null +++ b/smart-contract-verifier/smart-contract-verifier/src/zksync/zksolc_standard_json/output/source.rs @@ -0,0 +1,17 @@ +//! +//! The `solc --standard-json` output source. +//! + +use serde::{Deserialize, Serialize}; + +/// +/// The `solc --standard-json` output source. +/// +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Source { + /// The source code ID. + pub id: usize, + /// The source code AST. + pub ast: Option, +} diff --git a/smart-contract-verifier/smart-contract-verifier/tests/types.rs b/smart-contract-verifier/smart-contract-verifier/tests/types.rs index c548ce04c..dc171e8a1 100644 --- a/smart-contract-verifier/smart-contract-verifier/tests/types.rs +++ b/smart-contract-verifier/smart-contract-verifier/tests/types.rs @@ -3,7 +3,7 @@ pub mod solidity { use foundry_compilers::{CompilerInput, EvmVersion}; use smart_contract_verifier::{ solidity::{multi_part, standard_json}, - Version as CompilerVersion, + DetailedVersion as CompilerVersion, }; use std::{collections::BTreeMap, path::PathBuf, str::FromStr}; @@ -95,7 +95,7 @@ pub mod solidity { pub mod vyper { use bytes::Bytes; use foundry_compilers::EvmVersion; - use smart_contract_verifier::{vyper::multi_part, Version as CompilerVersion}; + use smart_contract_verifier::{vyper::multi_part, DetailedVersion as CompilerVersion}; use std::{collections::BTreeMap, path::PathBuf, str::FromStr}; pub struct VerificationRequest {