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

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open

feat(zk): zksolc linking #800

wants to merge 48 commits into from

Conversation

Karrq
Copy link
Contributor

@Karrq Karrq commented Dec 20, 2024

What 💻

  • Add functions to use zksolc --link
  • Process deploy-time linking (CREATE only)
  • Update default zksolc to 1.5.8

Why ✋

  • Allow tests & scripts to rely on contracts with libraries that have not been deployed yet

Notes 📝

There's a simple merge conflicts due to the lack of access to DualCompiledContracts during the MultiContractRunner instantiation.

This is a working yet incomplete implementation, as such there are a few quirks, in particular: unlinked factory deps are not supported (zksolc 1.5.9 should have some extra info that we can use), linking via CREATE2 is not supported either yet.

Currently there's an issue with trying to actually run the library, I believe due to a missing storage migration:

2024-12-20T21:22:57.842992Z ERROR suite{name=CounterTest}:test{kind=test name=testIncrement}:call:inspect: foundry_zksync_core::vm::tracers::cheatcode: call may fail or behave unexpectedly due to empty code target=0x0e93455c2327dc24ac46d3a2edade6dbb6260629 calldata="771602f70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

@Karrq Karrq self-assigned this Dec 20, 2024
@Karrq Karrq requested a review from a team as a code owner December 20, 2024 21:37
crates/evm/evm/src/executors/strategy.rs Show resolved Hide resolved
crates/forge/src/multi_runner.rs Outdated Show resolved Hide resolved
crates/linking/src/lib.rs Outdated Show resolved Hide resolved
crates/linking/src/lib.rs Outdated Show resolved Hide resolved
crates/strategy/zksync/src/executor.rs Outdated Show resolved Hide resolved
crates/zksync/compilers/src/link.rs Show resolved Hide resolved
crates/zksync/compilers/src/link.rs Outdated Show resolved Hide resolved
crates/zksync/compilers/src/link.rs Outdated Show resolved Hide resolved
crates/zksync/compilers/src/link.rs Outdated Show resolved Hide resolved
crates/zksync/core/src/lib.rs Show resolved Hide resolved
crates/config/src/zksync.rs Outdated Show resolved Hide resolved
.find(|(id, _)| id.source == needle.source && id.name == needle.name)
.map(|(_, evm)| (needle, zk, evm))
})
.filter(|(_, zk, evm)| zk.bytecode.is_some() && evm.bytecode.is_some())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be more of an assertion than a filter? Is it ok to keep on executing if we are missing some of the bytecodes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would simply not be added to the dual_compiled_contracts like it was before if it was unlinked (read as invalid bytecode)

Copy link
Contributor

@elfedy elfedy Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I am just worried we would filter something we should be aware it's missing (for example what happened when using zksolc previous to 1.5.7 and trying to just deserialize the eravm field to get the bytecode) and then this fails later when trying to get the bytecode and takes a bit to debug and track it is being filetred here. If this cannot happen then we are good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also some contracts which aren't meant to have bytecode like interfaces etc... Not sure the implications here tbh

crates/linking/src/lib.rs Outdated Show resolved Hide resolved
crates/linking/src/lib.rs Outdated Show resolved Hide resolved
crates/linking/src/lib.rs Outdated Show resolved Hide resolved
Karrq added 6 commits January 13, 2025 19:02
fix(strategy): remove get_mut for `DualCompiledContracts`
fix(zk:link): use version from config
refactor(zk:config): extract config -> zksolc compiler logic into
function
chore: lints
Karrq added 4 commits January 14, 2025 21:52
feat(script:zk): link with zksolc
test(zk:link): use 1.5.9 for deploy-time linking
feat(zk:link): detect factory dependencies as link references for
library lookup
fix(zk:link): require 1.5.9 for deploy-time-linking
@Karrq Karrq requested review from elfedy and nbaztec January 16, 2025 14:03
Karrq added 10 commits January 16, 2025 20:13
fix(link:zk): consistent artifact id prefixes
chore: formatting
refactor(link:zk): improve link targets filtering
fix(link:zk): don't preemptively strip file prefixes
fix(link:zk): better factory dep detection
chore: formatting
feat(strategy): return "broadcastable" tx
feat(link:zk): populate newly linked factory deps
feat(script): use `deploy_library`
fix(lib:zk): remove EVM lib deployments from broadcastable txs
refactor(artifact:zk): `all_factory_deps` method
chore: formatting
feat(link:script): register target & deps
Copy link
Contributor

@Jrigada Jrigada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job as always

@@ -68,9 +109,32 @@ pub trait ExecutorStrategyRunner: Debug + Send + Sync + ExecutorStrategyExt {
amount: U256,
) -> BackendResult<()>;

fn get_balance(&self, executor: &mut Executor, address: Address) -> BackendResult<U256>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do we use this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

during zksync library deployment, I made our strategy to snapshot the nonce&balance before the lib deployment, to then restore them for EraVM, so the "co-deployment" happens with the same input parameters


// manually increase nonce when performing CALLs
executor
.set_nonce(from, nonce + 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep in mind that when the nonce PR from @nbaztec is merged we have to change this don't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was migrated from upstream's code, but the meaning is that EVM won't increase the nonce for just calls (only create or full tx, but here it's not a full tx), so foundry increments the nonce manually to account for that, since here it's a call to the deployer

crates/linking/src/zksync.rs Outdated Show resolved Hide resolved
crates/linking/src/zksync.rs Outdated Show resolved Hide resolved
Copy link
Contributor Author

@Karrq Karrq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left comments to perhaps clarify some logic or choices

@@ -48,6 +61,34 @@ pub struct ExecutorStrategy {
pub context: Box<dyn ExecutorStrategyContext>,
}

pub struct LinkOutput {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally would like to move this stuff to a separate module, perhaps even a separate trait from the ExecutorStrategy?

fn set_nonce(&self, executor: &mut Executor, address: Address, nonce: u64)
-> BackendResult<()>;

fn get_nonce(&self, executor: &mut Executor, address: Address) -> BackendResult<u64>;

fn link(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used in MultiContractRunnerBuilder::build only, as scripts do not make use of a strategy

crates/evm/evm/src/executors/strategy.rs Show resolved Hide resolved

let linked_contracts = linker.get_linked_artifacts(&libraries)?;

// Create a mapping of name => (abi, deployment code, Vec<library deployment code>)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

migrated from existing code, but to me the comment looks outdated (no library deployment code)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other than clarifying a comment, optimizations

@@ -40,7 +40,7 @@ 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);
pub const ZKSOLC_VERSION: Version = Version::new(1, 5, 8);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, waiting for base to be >=1.5.9 as default

@@ -54,13 +54,15 @@ pub enum ZkSolcError {
/// `sendtransfer` error: Using `send()` or `transfer()` methods on `address payable` instead
/// of `call()`.
SendTransfer,
AssemblyCreate,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added just to be able to use 1.5.9 in tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't be needed once #840 is merged with base

@@ -69,7 +73,7 @@ impl<'a> FindBytecodeResult<'a> {
/// A collection of `[DualCompiledContract]`s
#[derive(Debug, Default, Clone)]
pub struct DualCompiledContracts {
contracts: Vec<DualCompiledContract>,
contracts: HashMap<ContractInfo, DualCompiledContract>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was done to make searching easier, mostly for linking where we don't have full bytecodes but mostly paths & names
I also don't think it hurts to convert this to a map, helps to avoid duplicates (which were possible before)

}

/// Compute a CREATE2 address according to zksync
pub fn compute_create2_address(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't find an equivalent for this unlinke create

@@ -51,7 +51,10 @@ where
let caller = env.tx.caller;
let nonce = ZKVMData::new(&mut ecx).get_tx_nonce(caller);
let (transact_to, is_create) = match env.tx.transact_to {
TransactTo::Call(to) => (to.to_h160(), false),
TransactTo::Call(to) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had to change this one since transact may get deployments of the libs now, and we need to have the outcome parsed appropriately
technically speaking this is also helpful for broadcastable txs, as those could also contain deployments in this form

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants