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 11 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
14 changes: 12 additions & 2 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,18 @@ impl ProjectCompiler {
let files = self.files.clone();

{
let zksolc_version = ZkSolc::get_version_for_path(&project.compiler.zksolc)?;
Report::new(SpinnerReporter::spawn_with(format!("Using zksolc-{zksolc_version}")));
let zksolc_current_version = ZkSolc::get_version_for_path(&project.compiler.zksolc)?;
let zksolc_min_supported_version = ZkSolc::zksolc_minimum_supported_version();
let zksolc_latest_supported_version = ZkSolc::zksolc_latest_supported_version();
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. Minimum version supported is v{zksolc_min_supported_version}")?;
elfedy marked this conversation as resolved.
Show resolved Hide resolved
hedgar2017 marked this conversation as resolved.
Show resolved Hide resolved
}
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. Latest version supported is v{zksolc_latest_supported_version}")?;
elfedy marked this conversation as resolved.
Show resolved Hide resolved
}
Report::new(SpinnerReporter::spawn_with(format!(
"Using zksolc-{zksolc_current_version}"
)));
}
self.zksync_compile_with(&project.paths.root, || {
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 metadata hash to the bytecode
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
23 changes: 14 additions & 9 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 @@ -128,7 +133,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 Expand Up @@ -198,7 +203,7 @@ pub fn config_create_project(
{
zksolc
} else if !config.offline {
let default_version = semver::Version::new(1, 5, 7);
let default_version = ZkSolc::zksolc_latest_supported_version();
let mut zksolc = ZkSolc::find_installed_version(&default_version)?;
if zksolc.is_none() {
ZkSolc::blocking_install(&default_version)?;
Expand Down Expand Up @@ -229,12 +234,12 @@ pub fn config_create_project(
fn config_solc_compiler(config: &Config) -> Result<SolcCompiler, SolcError> {
if let Some(path) = &config.zksync.solc_path {
if !path.is_file() {
return Err(SolcError::msg(format!("`solc` {} does not exist", path.display())))
return Err(SolcError::msg(format!("`solc` {} does not exist", path.display())));
}
let version = get_solc_version_info(path)?.version;
let solc =
Solc::new_with_version(path, Version::new(version.major, version.minor, version.patch));
return Ok(SolcCompiler::Specific(solc))
return Ok(SolcCompiler::Specific(solc));
}

if let Some(ref solc) = config.solc {
Expand All @@ -256,7 +261,7 @@ fn config_solc_compiler(config: &Config) -> Result<SolcCompiler, SolcError> {
}
SolcReq::Local(path) => {
if !path.is_file() {
return Err(SolcError::msg(format!("`solc` {} does not exist", path.display())))
return Err(SolcError::msg(format!("`solc` {} does not exist", path.display())));
}
let version = get_solc_version_info(path)?.version;
Solc::new_with_version(
Expand Down Expand Up @@ -307,7 +312,7 @@ pub fn config_ensure_zksolc(
if offline {
return Err(SolcError::msg(format!(
"can't install missing zksolc {version} in offline mode"
)))
)));
}
ZkSolc::blocking_install(version)?;
zksolc = ZkSolc::find_installed_version(version)?;
Expand All @@ -319,12 +324,12 @@ pub fn config_ensure_zksolc(
return Err(SolcError::msg(format!(
"`zksolc` {} does not exist",
zksolc.display()
)))
)));
}
Some(zksolc.clone())
}
};
return Ok(zksolc)
return Ok(zksolc);
}

Ok(None)
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
16 changes: 15 additions & 1 deletion crates/zksync/compilers/src/compilers/zksolc/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,23 @@ impl Default for ZkSolcInput {
}

impl ZkSolcInput {
fn new(language: SolcLanguage, sources: Sources, settings: ZkSettings) -> Self {
fn new(language: SolcLanguage, sources: Sources, mut settings: ZkSettings) -> Self {
// TODO: Right now we abuse the fact that zksolc ignores invalid fields. Whenever
elfedy marked this conversation as resolved.
Show resolved Hide resolved
// there are fields that, for the same feature, are different across compiler versions,
// we check and set them all to the same value. When compiling with a given version, the
// supported field is used and the other one is ignored.
// If this causes problems, we might need to make ZkSolcInput version aware and sanitize
// accordingly

// zksolc <= 1.5.6 has suppressed warnings/errors in at the root input level
let suppressed_warnings = settings.suppressed_warnings.clone();
let suppressed_errors = settings.suppressed_errors.clone();

// zksolc <= 1.5.6 uses "bytecode_hash" field for "hash_type"
if let Some(ref mut metadata) = settings.metadata {
metadata.sanitize();
};

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

Expand Down
48 changes: 41 additions & 7 deletions crates/zksync/compilers/src/compilers/zksolc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ 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, 7);

#[cfg(test)]
macro_rules! take_solc_installer_lock {
Expand Down Expand Up @@ -131,8 +127,8 @@ pub struct ZkSolcCompiler {

impl Default for ZkSolcCompiler {
fn default() -> Self {
let zksolc =
ZkSolc::get_path_for_version(&ZKSOLC_VERSION).expect("Could not install zksolc");
let zksolc = ZkSolc::get_path_for_version(&ZkSolc::zksolc_latest_supported_version())
.expect("Could not install zksolc");
Self { zksolc, solc: Default::default() }
}
}
Expand Down Expand Up @@ -417,6 +413,31 @@ 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 min_max_patch_by_minor_versions = vec![
(5, 6, 7), // 1.5.x
];
for (minor, min_patch, max_patch) in min_max_patch_by_minor_versions {
for i in min_patch..=max_patch {
ret.push(Version::new(1, minor, i));
}
}

ret
elfedy marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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![];
Expand Down Expand Up @@ -562,6 +583,18 @@ impl ZkSolc {

/// Get path for installed zksolc version. Returns `Ok(None)` if not installed
pub fn find_installed_version(version: &Version) -> Result<Option<PathBuf>> {
let min_supported_version = Self::zksolc_minimum_supported_version();
let latest_supported_version = Self::zksolc_latest_supported_version();
if *version < min_supported_version {
return Err(SolcError::msg(format!(
"Specifying zksolc v{version} not supported. Minimum version supported is v{min_supported_version}"
)));
}
if *version > latest_supported_version {
return Err(SolcError::msg(format!(
"Specifying zksolc v{version} not supported. Latest version supported is v{latest_supported_version}"
)));
}
let zksolc = Self::compiler_path(version)?;

if !zksolc.is_file() {
Expand Down Expand Up @@ -747,7 +780,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
32 changes: 20 additions & 12 deletions crates/zksync/compilers/src/compilers/zksolc/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,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 @@ -388,29 +386,34 @@ 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) {
self.bytecode_hash = self.hash_type;
}
}

Expand All @@ -425,6 +428,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 @@ -433,6 +439,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 @@ -443,6 +450,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
Loading