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(zk): zksolc linking #800

Merged
merged 69 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
d6489ae
feat(zk): zksolc linking
Karrq Dec 20, 2024
e5b805d
fix(zk:libs): calculate addresses w/ proper nonce
Karrq Jan 7, 2025
44aebad
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 7, 2025
f5dcfa1
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 8, 2025
262d2a7
fix: don't always assume DCC is present
Karrq Jan 8, 2025
b79cada
feat(executor): `deploy_library` in strategy
Karrq Jan 9, 2025
1941e33
fix(zk): create address computation
Karrq Jan 10, 2025
93babb9
chore: cleanup unused imports
Karrq Jan 10, 2025
5ed5154
test(zk): deploy-time linking (script/test)
Karrq Jan 10, 2025
018f3f0
chore: default zksolc to 1.5.8
Karrq Jan 13, 2025
c7f4f35
chore: lints
Karrq Jan 13, 2025
c1c2615
refactor: allow multiple lib deployments
Karrq Jan 13, 2025
1ec71e4
refactor(link): move to executor strategy
Karrq Jan 13, 2025
624119d
fix: compilation
Karrq Jan 13, 2025
8ba1e1c
feat(strategy:link): pass config
Karrq Jan 14, 2025
98c56fc
feat(zk:link): dedicated linker module
Karrq Jan 14, 2025
a81ee0c
chore: more lints
Karrq Jan 14, 2025
2152648
feat(zk:link): version check
Karrq Jan 14, 2025
da6caf6
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 14, 2025
71b78bd
chore: lints & fmt
Karrq Jan 14, 2025
14b2b0a
chore: more formatting
Karrq Jan 14, 2025
51590b9
fix(zk:link): retrieve factory dep hash
Karrq Jan 14, 2025
ef6b750
fix(zk:compilers): remove dead `libraries` module
Karrq Jan 14, 2025
119b94d
feat(link:zk): create2 linking
Karrq Jan 14, 2025
b3de768
chore: formatting
Karrq Jan 14, 2025
6d8dc25
feat(compiler:zk): `factory_dependencies_unlinked`
Karrq Jan 15, 2025
6362181
refactor: dual compiled contracts as map
Karrq Jan 16, 2025
f8ca806
feat(link:zk): recursive factory deps libs lookup
Karrq Jan 16, 2025
8a944b5
fix(link:zk): invert bool
Karrq Jan 16, 2025
2d1a6aa
fix(artifacts:zk): `is_unlinked` logic
Karrq Jan 17, 2025
32e2913
feat(strategy): `deploy_library` CREATE2 mode
Karrq Jan 17, 2025
b313feb
fix(zk:transact): detect direct deployments
Karrq Jan 17, 2025
9563833
feat(script:zk): match CREATE/CREATE2 with EVM
Karrq Jan 17, 2025
7e2a2d8
fix(zk:libs): encode extra metadata
Karrq Jan 20, 2025
3a25134
refactor(executors): `DeployLibResult`
Karrq Jan 20, 2025
3c39167
fix(artifact:zk): avoid underflow
Karrq Jan 20, 2025
7738ed9
fix(test:zk): proper stripping during link
Karrq Jan 20, 2025
5e04426
chore: fix spelling & docs
Karrq Jan 21, 2025
9154294
fix(test:script): avoid expecting create2 output
Karrq Jan 21, 2025
2872620
chore: clippy
Karrq Jan 21, 2025
16ffced
chore: formatting
Karrq Jan 21, 2025
bdce3c3
chore: codespell
Karrq Jan 21, 2025
72ca952
fix(artifacts:zk): `is_unlinked` underflow
Karrq Jan 21, 2025
7000890
feat(compiler:zk): `objectFormat`
Karrq Jan 21, 2025
e8c9ba0
fix(script:link:zk): skip version check if no libs
Karrq Jan 21, 2025
219e62f
fix(test:link:zk): avoid version check w/o libs
Karrq Jan 21, 2025
0331cd8
chore: clippy
Karrq Jan 21, 2025
0314731
fix(compiler:zk): optional object_format
Karrq Jan 21, 2025
b29c5f6
fix(link:zk): ignore target version for lookup
Karrq Jan 22, 2025
6e50097
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 22, 2025
d6b9bc3
fix(link:zk): proper EVM deployed_bc/bc
Karrq Jan 22, 2025
17fd4b4
chore: fmt
Karrq Jan 22, 2025
72bba3d
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 22, 2025
02e18e6
chore: fmt
Karrq Jan 23, 2025
8aa4479
test(zk): `DualCompiledContracts::find` units
Karrq Jan 23, 2025
81c6cf8
fix: clippy
Karrq Jan 23, 2025
f8a4f95
refactor(strategy): dedicated linking module
Karrq Jan 23, 2025
dec820b
fix: forgot to commit the new files
Karrq Jan 24, 2025
eea5fb8
chore: fmt
Karrq Jan 24, 2025
7a75263
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 24, 2025
b41be0d
fix(zk:link): new nonce types
Karrq Jan 24, 2025
91eb6e7
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 24, 2025
bee4b2a
Merge remote-tracking branch 'origin/main' into feat/zksolc-link
Karrq Jan 27, 2025
21b9920
test(zk): use default zksolc version normally
Karrq Jan 27, 2025
a48bfe5
docs: add notes on diverging sections
Karrq Jan 27, 2025
fbf900b
fix(zk): remove duplicate code from merge
Karrq Jan 27, 2025
7046a3a
refactor(script:zk): move linking to own module
Karrq Jan 27, 2025
72b7f27
chore: fmt
Karrq Jan 27, 2025
4b2f052
chore: clippy
Karrq Jan 27, 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
7 changes: 6 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 6 additions & 70 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ use foundry_compilers::{
solc::SolcSettings,
Artifact, Project, ProjectBuilder, ProjectCompileOutput, ProjectPathsConfig, SolcConfig,
};
use foundry_zksync_compilers::{
compilers::{
artifact_output::zk::ZkArtifactOutput,
zksolc::{ZkSolc, ZkSolcCompiler},
},
libraries::{self, ZkMissingLibrary},
use foundry_zksync_compilers::compilers::{
artifact_output::zk::ZkArtifactOutput,
zksolc::{ZkSolc, ZkSolcCompiler},
};

use num_format::{Locale, ToFormattedString};
use std::{
collections::{BTreeMap, HashSet},
collections::BTreeMap,
fmt::Display,
io::IsTerminal,
path::{Path, PathBuf},
Expand Down Expand Up @@ -336,7 +333,7 @@ impl ProjectCompiler {
let zksolc_version = ZkSolc::get_version_for_path(&project.compiler.zksolc)?;
Report::new(SpinnerReporter::spawn_with(format!("Using zksolc-{zksolc_version}")));
}
self.zksync_compile_with(&project.paths.root, || {
self.zksync_compile_with(|| {
let files_to_compile =
if !files.is_empty() { files } else { project.paths.input_files() };
let sources = Source::read_all(files_to_compile)?;
Expand All @@ -349,7 +346,6 @@ impl ProjectCompiler {
#[instrument(target = "forge::compile", skip_all)]
fn zksync_compile_with<F>(
self,
root_path: impl AsRef<Path>,
f: F,
) -> Result<ProjectCompileOutput<ZkSolcCompiler, ZkArtifactOutput>>
where
Expand Down Expand Up @@ -394,7 +390,7 @@ impl ProjectCompiler {
sh_println!("{output}")?;
}

self.zksync_handle_output(root_path, &output)?;
self.zksync_handle_output(&output)?;
}

Ok(output)
Expand All @@ -403,71 +399,11 @@ impl ProjectCompiler {
/// If configured, this will print sizes or names
fn zksync_handle_output(
&self,
root_path: impl AsRef<Path>,
output: &ProjectCompileOutput<ZkSolcCompiler, ZkArtifactOutput>,
) -> Result<()> {
let print_names = self.print_names.unwrap_or(false);
let print_sizes = self.print_sizes.unwrap_or(false);

// Process missing libraries
// TODO: skip this if project was not compiled using --detect-missing-libraries
let mut missing_libs_unique: HashSet<String> = HashSet::new();
for (artifact_id, artifact) in output.artifact_ids() {
// TODO: when compiling specific files, the output might still add cached artifacts
// that are not part of the file list to the output, which may cause missing libraries
// error to trigger for files that were not intended to be compiled.
// This behaviour needs to be investigated better on the foundry-compilers side.
// For now we filter, checking only the files passed to compile.
let is_target_file =
self.files.is_empty() || self.files.iter().any(|f| artifact_id.path == *f);
if is_target_file {
if let Some(mls) = artifact.missing_libraries() {
missing_libs_unique.extend(mls.clone());
}
}
}

let missing_libs: Vec<ZkMissingLibrary> = missing_libs_unique
.into_iter()
.map(|ml| {
let mut split = ml.split(':');
let contract_path =
split.next().expect("Failed to extract contract path for missing library");
let contract_name =
split.next().expect("Failed to extract contract name for missing library");

let mut abs_path_buf = PathBuf::new();
abs_path_buf.push(root_path.as_ref());
abs_path_buf.push(contract_path);

let art = output.find(abs_path_buf.as_path(), contract_name).unwrap_or_else(|| {
panic!(
"Could not find contract {contract_name} at path {contract_path} for compilation output"
)
});

ZkMissingLibrary {
contract_path: contract_path.to_string(),
contract_name: contract_name.to_string(),
missing_libraries: art.missing_libraries().cloned().unwrap_or_default(),
}
})
.collect();

if !missing_libs.is_empty() {
libraries::add_dependencies_to_missing_libraries_cache(
root_path,
missing_libs.as_slice(),
)
.expect("Error while adding missing libraries");
let missing_libs_list = missing_libs
.iter()
.map(|ml| format!("{}:{}", ml.contract_path, ml.contract_name))
.collect::<Vec<String>>()
.join(", ");
eyre::bail!("Missing libraries detected: {missing_libs_list}\n\nRun the following command in order to deploy each missing library:\n\nforge create <LIBRARY> --private-key <PRIVATE_KEY> --rpc-url <RPC_URL> --chain <CHAIN_ID> --zksync\n\nThen pass the library addresses using the --libraries option");
}

// print any sizes or names
if print_names {
let mut artifacts: BTreeMap<_, Vec<_>> = BTreeMap::new();
Expand Down
54 changes: 31 additions & 23 deletions crates/config/src/zksync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,30 @@ pub fn config_zksolc_settings(config: &Config) -> Result<ZkSolcSettings, SolcErr
Ok(config.zksync.settings(libraries, config.evm_version, config.via_ir))
}

/// Return the configured `zksolc` compiler
///
/// If not `offline`, will install the default version automatically
/// Will fallback to `zksolc` present in the environment
pub fn config_zksolc_compiler(config: &Config) -> Result<ZkSolcCompiler, SolcError> {
let zksolc = if let Some(zksolc) =
config_ensure_zksolc(config.zksync.zksolc.as_ref(), config.offline)?
{
zksolc
} else if !config.offline {
let default_version = semver::Version::new(1, 5, 8);
let mut zksolc = ZkSolc::find_installed_version(&default_version)?;
if zksolc.is_none() {
ZkSolc::blocking_install(&default_version)?;
zksolc = ZkSolc::find_installed_version(&default_version)?;
}
zksolc.unwrap_or_else(|| panic!("Could not install zksolc v{default_version}"))
} else {
"zksolc".into()
};

Ok(ZkSolcCompiler { zksolc, solc: config_solc_compiler(config)? })
}

/// Create a new zkSync project
pub fn config_create_project(
config: &Config,
Expand Down Expand Up @@ -193,23 +217,7 @@ pub fn config_create_project(
builder = builder.sparse_output(filter);
}

let zksolc = if let Some(zksolc) =
config_ensure_zksolc(config.zksync.zksolc.as_ref(), config.offline)?
{
zksolc
} else if !config.offline {
let default_version = semver::Version::new(1, 5, 7);
let mut zksolc = ZkSolc::find_installed_version(&default_version)?;
if zksolc.is_none() {
ZkSolc::blocking_install(&default_version)?;
zksolc = ZkSolc::find_installed_version(&default_version)?;
}
zksolc.unwrap_or_else(|| panic!("Could not install zksolc v{default_version}"))
} else {
"zksolc".into()
};

let zksolc_compiler = ZkSolcCompiler { zksolc, solc: config_solc_compiler(config)? };
let zksolc_compiler = config_zksolc_compiler(config)?;

let project = builder.build(zksolc_compiler)?;

Expand All @@ -229,12 +237,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 +264,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 +315,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 +327,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
1 change: 1 addition & 0 deletions crates/evm/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ foundry-evm-core.workspace = true
foundry-evm-coverage.workspace = true
foundry-evm-fuzz.workspace = true
foundry-evm-traces.workspace = true
foundry-linking.workspace = true
foundry-zksync-core.workspace = true
foundry-zksync-compilers.workspace = true
foundry-zksync-inspectors.workspace = true
Expand Down
19 changes: 18 additions & 1 deletion crates/evm/evm/src/executors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use std::{
borrow::Cow,
time::{Duration, Instant},
};
use strategy::ExecutorStrategy;
use strategy::{DeployLibKind, DeployLibResult, ExecutorStrategy};

mod builder;
pub use builder::ExecutorBuilder;
Expand Down Expand Up @@ -303,6 +303,23 @@ impl Executor {
self.deploy_with_env(env, rd)
}

/// Deploys a library contract and commits the new state to the underlying database.
///
/// Executes a `deploy_kind` transaction with the provided parameters
/// and persistent database state modifications.
///
/// Will return a list of deployment results and transaction requests
/// Will also ensure nonce is increased for the sender
pub fn deploy_library(
&mut self,
from: Address,
kind: DeployLibKind,
value: U256,
rd: Option<&RevertDecoder>,
) -> Result<Vec<DeployLibResult>, EvmError> {
self.strategy.runner.deploy_library(self, from, kind, value, rd)
}

/// Deploys a contract using the given `env` and commits the new state to the underlying
/// database.
///
Expand Down
Loading
Loading