Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement compiler backwards compatibility policy #843

Merged
merged 24 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
62d8384
add zksolc version list and basic compatibility test
elfedy Jan 13, 2025
6fd4cc7
Warn about minimum supported version
elfedy Jan 14, 2025
0fb4cb2
Add backwards compatibility for hash type
elfedy Jan 15, 2025
7a7a7dc
add latest supported version and an interface to sanitize bytecode hash
elfedy Jan 16, 2025
f819d99
use duplicate field instead of alias
elfedy Jan 16, 2025
2eb60f2
Remove stale import
elfedy Jan 16, 2025
449e14e
fix typo
elfedy Jan 16, 2025
84294af
improve wording
elfedy Jan 16, 2025
6f19010
update docs
elfedy Jan 16, 2025
67861ec
moar update docs
elfedy Jan 16, 2025
01eb12a
Fail when specifying unsupported versions
elfedy Jan 17, 2025
df815ab
Update crates/common/src/compile.rs
elfedy Jan 22, 2025
26d7573
Update crates/common/src/compile.rs
elfedy Jan 22, 2025
8ff768a
Update crates/zksync/compilers/src/compilers/zksolc/input.rs
elfedy Jan 22, 2025
727a8ac
Merge branch 'main' into elfedy-compiler-support
elfedy Jan 22, 2025
93123a0
Use ranges to specify version ranges
elfedy Jan 22, 2025
1623bc9
Merge branch 'main' into elfedy-compiler-support
nbaztec Jan 24, 2025
dadfa01
Merge branch 'main' into elfedy-compiler-support
elfedy Jan 28, 2025
bceb7e1
Merge branch 'main' into elfedy-compiler-support
elfedy Jan 29, 2025
21a9193
make things compile again
elfedy Jan 29, 2025
64c09e7
implement version aware sanitizing
elfedy Jan 29, 2025
5cb35d5
Merge branch 'elfedy-compiler-support' of github.com:matter-labs/foun…
elfedy Jan 29, 2025
eb50b70
Unsupport 1.5.9 and add deprecation warning
elfedy Jan 30, 2025
83777f7
fix wrong solc major version
elfedy Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use foundry_compilers::{
Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, SolcConfig,
};
use foundry_zksync_compilers::compilers::{
artifact_output::zk::ZkArtifactOutput, zksolc::ZkSolcCompiler,
artifact_output::zk::ZkArtifactOutput,
zksolc::{ZkSolc, ZkSolcCompiler, ZKSOLC_UNSUPPORTED_VERSIONS},
};

use num_format::{Locale, ToFormattedString};
Expand Down Expand Up @@ -330,8 +331,21 @@ impl ProjectCompiler {
let files = self.files.clone();

{
let zksolc_version = project.settings.zksolc_version_ref();
Report::new(SpinnerReporter::spawn_with(format!("Using zksolc-{zksolc_version}")));
let zksolc_current_version = project.settings.zksolc_version_ref();
let zksolc_min_supported_version = ZkSolc::zksolc_minimum_supported_version();
let zksolc_latest_supported_version = ZkSolc::zksolc_latest_supported_version();
if ZKSOLC_UNSUPPORTED_VERSIONS.contains(zksolc_current_version) {
sh_warn!("Compiling with zksolc v{zksolc_current_version} which is not supported and may lead to unexpected errors. Specifying an unsupported version is deprecated and will return an error in future versions of foundry-zksync.")?;
}
if zksolc_current_version < &zksolc_min_supported_version {
sh_warn!("Compiling with zksolc v{zksolc_current_version} which is not supported and may lead to unexpected errors. Specifying an unsupported version is deprecated and will return an error in future versions of foundry-zksync. Minimum version supported is v{zksolc_min_supported_version}")?;
}
if zksolc_current_version > &zksolc_latest_supported_version {
sh_warn!("Compiling with zksolc v{zksolc_current_version} which is still not supported and may lead to unexpected errors. Specifying an unsupported version is deprecated and will return an error in future versions of foundry-zksync. Latest version supported is v{zksolc_latest_supported_version}")?;
}
Report::new(SpinnerReporter::spawn_with(format!(
"Using zksolc-{zksolc_current_version}"
)));
}
self.zksync_compile_with(|| {
let files_to_compile =
Expand Down
7 changes: 4 additions & 3 deletions crates/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ no_storage_caching = false
# Whether to store the referenced sources in the metadata as literal data.
use_literal_content = false
# use ipfs method to generate the metadata hash, solc's default.
# To not include the metadata hash, to allow for deterministic code: https://docs.soliditylang.org/en/latest/metadata.html, use "none"
# To not include the metadata hash, to allow for deterministic code: https://docs.soliditylang.org/en/latest/metadata.html, use "none" (evm compilation only, field will be ignored for zksync)
bytecode_hash = "ipfs"
# Whether to append the CBOR-encoded metadata file.
cbor_metadata = true
Expand Down Expand Up @@ -228,11 +228,12 @@ The `zksync` settings must be prefixed with the profile they correspond to:
compile = false
# Enable zkVM at startup, needs `compile = true` to have effect
startup = true
# By default the latest version is used
# By default the latest supported version is used
zksolc = "1.5.0"
# By default the corresponding solc patched version from matter-labs is used
solc_path = "./solc-0.8.23-1.0.1"
bytecode_hash = "none"
# By default, no value is passed and the default for the compiler (keccak256) will be used
hash_type = "none"
# Allow compiler to use mode 'z' if contracts won't fit in the EraVM bytecode
# size limitations
fallback_oz = false
Expand Down
9 changes: 7 additions & 2 deletions crates/config/src/zksync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ pub struct ZkSyncConfig {
/// solc path to use along the zksolc compiler
pub solc_path: Option<PathBuf>,

/// Whether to include the metadata hash for zksolc compiled bytecode.
/// Hash type for the the metadata hash appended by zksolc to the compiled bytecode.
pub hash_type: Option<BytecodeHash>,

/// Hash type for the the metadata hash appended by zksolc to the compiled bytecode.
/// Deprecated in favor of `hash_type`
elfedy marked this conversation as resolved.
Show resolved Hide resolved
pub bytecode_hash: Option<BytecodeHash>,

/// Whether to try to recompile with -Oz if the bytecode is too large.
Expand Down Expand Up @@ -83,6 +87,7 @@ impl Default for ZkSyncConfig {
startup: false,
zksolc: Default::default(),
solc_path: Default::default(),
hash_type: Default::default(),
bytecode_hash: Default::default(),
fallback_oz: Default::default(),
enable_eravm_extensions: Default::default(),
Expand Down Expand Up @@ -129,7 +134,7 @@ impl ZkSyncConfig {
libraries,
optimizer,
evm_version: Some(evm_version),
metadata: Some(SettingsMetadata { bytecode_hash: self.bytecode_hash }),
metadata: Some(SettingsMetadata::new(self.hash_type.or(self.bytecode_hash))),
via_ir: Some(via_ir),
// Set in project paths.
remappings: Vec::new(),
Expand Down
2 changes: 1 addition & 1 deletion crates/verify/src/etherscan/flatten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource {
context: &ZkVerificationContext,
) -> Result<(String, String, CodeFormat)> {
let metadata = context.project.settings.settings.metadata.as_ref();
let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default();
let bch = metadata.and_then(|m| m.hash_type).unwrap_or_default();

eyre::ensure!(
bch == foundry_zksync_compilers::compilers::zksolc::settings::BytecodeHash::Keccak256,
Expand Down
25 changes: 21 additions & 4 deletions crates/zksync/compilers/src/compilers/zksolc/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ impl CompilerInput for ZkSolcVersionedInput {
version: Version,
) -> Self {
let zksolc_path = settings.zksolc_path();
let zksolc_version = settings.zksolc_version_ref().clone();
let ZkSolcSettings { settings, cli_settings, .. } = settings;
let input = ZkSolcInput::new(language, sources, settings).sanitized(&version);
let input =
ZkSolcInput::new(language, sources, settings, &zksolc_version).sanitized(&version);

Self { solc_version: version, input, cli_settings, zksolc_path }
}
Expand Down Expand Up @@ -109,9 +111,24 @@ impl Default for ZkSolcInput {
}

impl ZkSolcInput {
fn new(language: SolcLanguage, sources: Sources, settings: ZkSettings) -> Self {
let suppressed_warnings = settings.suppressed_warnings.clone();
let suppressed_errors = settings.suppressed_errors.clone();
fn new(
language: SolcLanguage,
sources: Sources,
mut settings: ZkSettings,
zksolc_version: &Version,
) -> Self {
let mut suppressed_warnings = HashSet::default();
let mut suppressed_errors = HashSet::default();
// zksolc <= 1.5.6 has suppressed warnings/errors in at the root input level
if zksolc_version <= &Version::new(1, 5, 6) {
suppressed_warnings = std::mem::take(&mut settings.suppressed_warnings);
suppressed_errors = std::mem::take(&mut settings.suppressed_errors);
}

if let Some(ref mut metadata) = settings.metadata {
metadata.sanitize(zksolc_version);
};

Self { language, sources, settings, suppressed_warnings, suppressed_errors }
}

Expand Down
47 changes: 37 additions & 10 deletions crates/zksync/compilers/src/compilers/zksolc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ pub mod input;
pub mod settings;
pub use settings::{ZkSettings, ZkSolcSettings};

/// zksolc command
pub const ZKSOLC: &str = "zksolc";
/// ZKsync solc release used for all ZKsync solc versions
pub const ZKSYNC_SOLC_RELEASE: Version = Version::new(1, 0, 1);
/// Default zksolc version
pub const ZKSOLC_VERSION: Version = Version::new(1, 5, 11);

/// Get zksolc versions that are specifically not supported
pub const ZKSOLC_UNSUPPORTED_VERSIONS: [Version; 1] = [Version::new(1, 5, 9)];

#[cfg(test)]
macro_rules! take_solc_installer_lock {
Expand Down Expand Up @@ -408,14 +407,41 @@ impl ZkSolc {
}
}

/// Get supported zksolc versions
pub fn zksolc_supported_versions() -> Vec<Version> {
elfedy marked this conversation as resolved.
Show resolved Hide resolved
let mut ret = vec![];
let version_ranges = vec![(1, 5, 6..=11)];

for (major, minor, patch_range) in version_ranges {
for patch in patch_range {
let v = Version::new(major, minor, patch);
if !ZKSOLC_UNSUPPORTED_VERSIONS.contains(&v) {
ret.push(v);
}
}
}

ret
}

/// Get zksolc minimum supported version
pub fn zksolc_minimum_supported_version() -> Version {
ZkSolc::zksolc_supported_versions().remove(0)
elfedy marked this conversation as resolved.
Show resolved Hide resolved
}

/// Get zksolc minimum supported version
pub fn zksolc_latest_supported_version() -> Version {
ZkSolc::zksolc_supported_versions().pop().expect("No supported zksolc versions")
elfedy marked this conversation as resolved.
Show resolved Hide resolved
}

/// Get available zksync solc versions
pub fn solc_available_versions() -> Vec<Version> {
let mut ret = vec![];
let min_max_patch_by_minor_versions =
vec![(4, 12, 26), (5, 0, 17), (6, 0, 12), (7, 0, 6), (8, 0, 28)];
for (minor, min_patch, max_patch) in min_max_patch_by_minor_versions {
for i in min_patch..=max_patch {
ret.push(Version::new(0, minor, i));
let version_ranges =
vec![(0, 4, 12..=26), (0, 5, 0..=17), (0, 6, 0..=12), (0, 7, 0..=6), (0, 8, 0..=28)];
for (major, minor, patch_range) in version_ranges {
for patch in patch_range {
ret.push(Version::new(major, minor, patch));
}
}

Expand Down Expand Up @@ -738,7 +764,8 @@ mod tests {
use super::*;

fn zksolc() -> ZkSolc {
let zksolc_path = ZkSolc::get_path_for_version(&ZKSOLC_VERSION).unwrap();
let zksolc_path =
ZkSolc::get_path_for_version(&ZkSolc::zksolc_latest_supported_version()).unwrap();
let solc_version = "0.8.27";

take_solc_installer_lock!(_lock);
Expand Down
42 changes: 27 additions & 15 deletions crates/zksync/compilers/src/compilers/zksolc/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::{
str::FromStr,
};

use super::{ZkSolc, ZKSOLC_VERSION};
use super::ZkSolc;
///
/// The Solidity compiler codegen.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
Expand All @@ -43,8 +43,6 @@ pub struct ZkSettings {
/// The Solidity codegen.
#[serde(default)]
pub codegen: Codegen,
// TODO: era-compiler-solidity uses a BTreeSet of strings. In theory the serialization
// should be the same but maybe we should double check
/// Solidity remappings
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub remappings: Vec<Remapping>,
Expand Down Expand Up @@ -110,12 +108,13 @@ pub struct ZkSolcSettings {

impl Default for ZkSolcSettings {
fn default() -> Self {
let zksolc_path = ZkSolc::get_path_for_version(&ZKSOLC_VERSION)
let version = ZkSolc::zksolc_latest_supported_version();
let zksolc_path = ZkSolc::get_path_for_version(&version)
.expect("failed getting default zksolc version path");
Self {
settings: Default::default(),
cli_settings: Default::default(),
zksolc_version: ZKSOLC_VERSION,
zksolc_version: version,
zksolc_path,
}
}
Expand Down Expand Up @@ -400,29 +399,37 @@ impl OptimizerDetails {

/// Settings metadata
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SettingsMetadata {
/// Use the given hash method for the metadata hash that is appended to the bytecode.
/// The metadata hash can be removed from the bytecode via option "none".
/// `zksolc` only supports keccak256
#[serde(
default,
rename = "bytecodeHash",
skip_serializing_if = "Option::is_none",
with = "serde_helpers::display_from_str_opt"
)]
pub bytecode_hash: Option<BytecodeHash>,
pub hash_type: Option<BytecodeHash>,
/// hash_type field name for zksolc v1.5.6 and older
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "serde_helpers::display_from_str_opt"
)]
bytecode_hash: Option<BytecodeHash>,
}

impl SettingsMetadata {
/// New SettingsMetadata
pub fn new(hash: BytecodeHash) -> Self {
Self { bytecode_hash: Some(hash) }
/// Creates new SettingsMettadata
pub fn new(hash_type: Option<BytecodeHash>) -> Self {
Self { hash_type, bytecode_hash: None }
}
}

impl From<BytecodeHash> for SettingsMetadata {
fn from(hash: BytecodeHash) -> Self {
Self { bytecode_hash: Some(hash) }
/// Makes SettingsMettadata version compatible
pub fn sanitize(&mut self, zksolc_version: &Version) {
// zksolc <= 1.5.6 uses "bytecode_hash" field for "hash_type"
if zksolc_version <= &Version::new(1, 5, 6) {
self.bytecode_hash = self.hash_type.take();
}
}
}

Expand All @@ -437,6 +444,9 @@ pub enum BytecodeHash {
/// The default keccak256 hash.
#[serde(rename = "keccak256")]
Keccak256,
/// The `ipfs` hash.
#[serde(rename = "ipfs")]
Ipfs,
elfedy marked this conversation as resolved.
Show resolved Hide resolved
}

impl FromStr for BytecodeHash {
Expand All @@ -445,6 +455,7 @@ impl FromStr for BytecodeHash {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"none" => Ok(Self::None),
"ipfs" => Ok(Self::Ipfs),
"keccak256" => Ok(Self::Keccak256),
s => Err(format!("Unknown bytecode hash: {s}")),
}
Expand All @@ -455,6 +466,7 @@ impl fmt::Display for BytecodeHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Keccak256 => "keccak256",
Self::Ipfs => "ipfs",
Self::None => "none",
};
f.write_str(s)
Expand Down
Loading