Skip to content

Commit

Permalink
feat: Handle new yul compilation flow (#3038)
Browse files Browse the repository at this point in the history
## What ❔

New versions of `zksolc` have several breaking changes. The following
changes require updates to the zksync-era:
1. New output path for yul and zasm contracts
(`<name>.yul/<name>.yul.zbin` instead of `<name>.yul.zbin`)
2. `.zbin` files now contain utf8 encoded bytecode instead of binary

This pull request adds support for these changes while maintaining
backwards compatibility.

<!-- What are the changes this PR brings about? -->
<!-- Example: This PR adds a PR template to the repo. -->
<!-- (For bigger PRs adding more context is appreciated) -->

## Why ❔

<!-- Why are these changes done? What goal do they contribute to? What
are the principles behind them? -->
<!-- Example: PR templates ensure PR reviewers, observers, and future
iterators are in context about the evolution of repos. -->

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `zk_supervisor fmt` and `zk_supervisor
lint`.
  • Loading branch information
0xVolosnikov authored Oct 10, 2024
1 parent e5b5a3b commit a13de0b
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 49 deletions.
54 changes: 45 additions & 9 deletions core/lib/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,19 +259,19 @@ impl SystemContractsRepo {
"artifacts-zk/contracts-preprocessed/{0}{1}.sol/{1}.json",
directory, name
))),
ContractLanguage::Yul => read_zbin_bytecode_from_path(self.root.join(format!(
"contracts-preprocessed/{0}artifacts/{1}.yul.zbin",
directory, name
))),
ContractLanguage::Yul => {
let artifacts_path = self
.root
.join(format!("contracts-preprocessed/{}artifacts/", directory));
read_yul_bytecode_by_path(artifacts_path, name)
}
}
}
}

pub fn read_bootloader_code(bootloader_type: &str) -> Vec<u8> {
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/build/artifacts/{}.yul.zbin",
bootloader_type
))
let artifacts_path = "contracts/system-contracts/bootloader/build/artifacts/";
read_yul_bytecode(artifacts_path, bootloader_type)
}

fn read_proved_batch_bootloader_bytecode() -> Vec<u8> {
Expand All @@ -288,10 +288,46 @@ pub fn read_zbin_bytecode(relative_zbin_path: impl AsRef<Path>) -> Vec<u8> {
read_zbin_bytecode_from_path(bytecode_path)
}

pub fn read_yul_bytecode(relative_artifacts_path: &str, name: &str) -> Vec<u8> {
let artifacts_path = Path::new(&home_path()).join(relative_artifacts_path);
read_yul_bytecode_by_path(artifacts_path, name)
}

pub fn read_yul_bytecode_by_path(artifacts_path: PathBuf, name: &str) -> Vec<u8> {
let bytecode_path = artifacts_path.join(format!("{name}.yul/{name}.yul.zbin"));

// Legacy versions of zksolc use the following path for output data if a yul file is being compiled: <name>.yul.zbin
// New zksolc versions use <name>.yul/<name>.yul.zbin, for consistency with solidity files compilation.
// In addition, the output of the legacy zksolc in this case is a binary file, while in new versions it is hex encoded.
if fs::exists(&bytecode_path)
.unwrap_or_else(|err| panic!("Invalid path: {bytecode_path:?}, {err}"))
{
read_zbin_bytecode_from_hex_file(bytecode_path)
} else {
let bytecode_path_legacy = artifacts_path.join(format!("{name}.yul.zbin"));

if fs::exists(&bytecode_path_legacy)
.unwrap_or_else(|err| panic!("Invalid path: {bytecode_path_legacy:?}, {err}"))
{
read_zbin_bytecode_from_path(bytecode_path_legacy)
} else {
panic!("Can't find bytecode for '{name}' yul contract at {artifacts_path:?}")
}
}
}

/// Reads zbin bytecode from a given path.
fn read_zbin_bytecode_from_path(bytecode_path: PathBuf) -> Vec<u8> {
fs::read(&bytecode_path)
.unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {:?}: {}", bytecode_path, err))
.unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {bytecode_path:?}: {err}"))
}

/// Reads zbin bytecode from a given path as utf8 text file.
fn read_zbin_bytecode_from_hex_file(bytecode_path: PathBuf) -> Vec<u8> {
let bytes = fs::read(&bytecode_path)
.unwrap_or_else(|err| panic!("Can't read .zbin bytecode at {bytecode_path:?}: {err}"));

hex::decode(bytes).unwrap_or_else(|err| panic!("Invalid input file: {bytecode_path:?}, {err}"))
}

/// Hash of code and code which consists of 32 bytes words
Expand Down
9 changes: 1 addition & 8 deletions core/lib/multivm/src/versions/vm_1_3_2/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use zk_evm_1_3_3::{
vm_state::PrimitiveValue,
zkevm_opcode_defs::FatPointer,
};
use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts};
use zksync_contracts::BaseSystemContracts;
use zksync_system_constants::ZKPORTER_IS_AVAILABLE;
use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256};
use zksync_utils::h256_to_u256;
Expand Down Expand Up @@ -221,13 +221,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) {
)
}

pub fn read_bootloader_test_code(test: &str) -> Vec<u8> {
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
))
}

pub(crate) fn calculate_computational_gas_used<
S: WriteStorage,
T: PubdataSpentTracer<H>,
Expand Down
8 changes: 3 additions & 5 deletions core/lib/multivm/src/versions/vm_fast/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use ethabi::Contract;
use once_cell::sync::Lazy;
use zksync_contracts::{
load_contract, read_bytecode, read_zbin_bytecode, BaseSystemContracts, SystemContractCode,
load_contract, read_bytecode, read_yul_bytecode, BaseSystemContracts, SystemContractCode,
};
use zksync_types::{
utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H160, H256,
Expand Down Expand Up @@ -64,10 +64,8 @@ pub(crate) fn read_test_contract() -> Vec<u8> {
}

pub(crate) fn get_bootloader(test: &str) -> SystemContractCode {
let bootloader_code = read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
));
let artifacts_path = "contracts/system-contracts/bootloader/tests/artifacts/";
let bootloader_code = read_yul_bytecode(artifacts_path, test);

let bootloader_hash = hash_bytecode(&bootloader_code);
SystemContractCode {
Expand Down
9 changes: 4 additions & 5 deletions core/lib/multivm/src/versions/vm_latest/tests/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use ethabi::Contract;
use once_cell::sync::Lazy;
use zksync_contracts::{
load_contract, read_bytecode, read_zbin_bytecode, BaseSystemContracts, SystemContractCode,
load_contract, read_bytecode, read_yul_bytecode, read_zbin_bytecode, BaseSystemContracts,
SystemContractCode,
};
use zksync_types::{
utils::storage_key_for_standard_token_balance, AccountTreeId, Address, StorageKey, H256, U256,
Expand Down Expand Up @@ -59,10 +60,8 @@ pub(crate) fn read_test_contract() -> Vec<u8> {
}

pub(crate) fn get_bootloader(test: &str) -> SystemContractCode {
let bootloader_code = read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
));
let artifacts_path = "contracts/system-contracts/bootloader/tests/artifacts/";
let bootloader_code = read_yul_bytecode(artifacts_path, test);

let bootloader_hash = hash_bytecode(&bootloader_code);
SystemContractCode {
Expand Down
9 changes: 1 addition & 8 deletions core/lib/multivm/src/versions/vm_m5/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use zk_evm_1_3_1::{
vm_state::PrimitiveValue,
zkevm_opcode_defs::FatPointer,
};
use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts};
use zksync_contracts::BaseSystemContracts;
use zksync_system_constants::ZKPORTER_IS_AVAILABLE;
use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256};
use zksync_utils::h256_to_u256;
Expand Down Expand Up @@ -253,13 +253,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) {
)
}

pub fn read_bootloader_test_code(test: &str) -> Vec<u8> {
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
))
}

/// Log query, which handle initial and repeated writes to the storage
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StorageLogQuery {
Expand Down
9 changes: 1 addition & 8 deletions core/lib/multivm/src/versions/vm_m6/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use zk_evm_1_3_1::{
vm_state::PrimitiveValue,
zkevm_opcode_defs::FatPointer,
};
use zksync_contracts::{read_zbin_bytecode, BaseSystemContracts};
use zksync_contracts::BaseSystemContracts;
use zksync_system_constants::ZKPORTER_IS_AVAILABLE;
use zksync_types::{Address, StorageLogKind, H160, MAX_L2_TX_GAS_LIMIT, U256};
use zksync_utils::h256_to_u256;
Expand Down Expand Up @@ -256,13 +256,6 @@ pub fn create_test_block_params() -> (BlockContext, BlockProperties) {
)
}

pub fn read_bootloader_test_code(test: &str) -> Vec<u8> {
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
))
}

pub(crate) fn calculate_computational_gas_used<
S: Storage,
T: PubdataSpentTracer<H>,
Expand Down
35 changes: 29 additions & 6 deletions core/tests/upgrade-test/tests/upgrade.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,21 @@ describe('Upgrade test', function () {
complexUpgraderAddress = '0x000000000000000000000000000000000000800f';

if (fileConfig.loadFromFile) {
const generalConfig = loadConfig({ pathToHome, chain: fileConfig.chain, config: 'general.yaml' });
const contractsConfig = loadConfig({ pathToHome, chain: fileConfig.chain, config: 'contracts.yaml' });
const secretsConfig = loadConfig({ pathToHome, chain: fileConfig.chain, config: 'secrets.yaml' });
const generalConfig = loadConfig({
pathToHome,
chain: fileConfig.chain,
config: 'general.yaml'
});
const contractsConfig = loadConfig({
pathToHome,
chain: fileConfig.chain,
config: 'contracts.yaml'
});
const secretsConfig = loadConfig({
pathToHome,
chain: fileConfig.chain,
config: 'secrets.yaml'
});

ethProviderAddress = secretsConfig.l1.l1_rpc_url;
web3JsonRpc = generalConfig.api.web3_json_rpc.http_url;
Expand All @@ -89,7 +101,11 @@ describe('Upgrade test', function () {
alice = tester.emptyWallet();

if (fileConfig.loadFromFile) {
const chainWalletConfig = loadConfig({ pathToHome, chain: fileConfig.chain, config: 'wallets.yaml' });
const chainWalletConfig = loadConfig({
pathToHome,
chain: fileConfig.chain,
config: 'wallets.yaml'
});

adminGovWallet = new ethers.Wallet(chainWalletConfig.governor.private_key, alice._providerL1());

Expand Down Expand Up @@ -220,8 +236,15 @@ describe('Upgrade test', function () {
});

step('Send l1 tx for saving new bootloader', async () => {
const path = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin`;
const bootloaderCode = ethers.hexlify(fs.readFileSync(path));
const path = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul/playground_batch.yul.zbin`;
let bootloaderCode;
if (fs.existsSync(path)) {
bootloaderCode = '0x'.concat(fs.readFileSync(path).toString());
} else {
const legacyPath = `${pathToHome}/contracts/system-contracts/bootloader/build/artifacts/playground_batch.yul.zbin`;
bootloaderCode = ethers.hexlify(fs.readFileSync(legacyPath));
}

bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode));
const txHandle = await tester.syncWallet.requestExecute({
contractAddress: ethers.ZeroAddress,
Expand Down

0 comments on commit a13de0b

Please sign in to comment.