Skip to content

Commit

Permalink
feat(contracts)!: integrate protocol defense changes (#2737)
Browse files Browse the repository at this point in the history
## What ❔

The work done in the protocol defense project introduced a number of
changes, namely custom errors in our solidity contracts. We need to
update the server code to handle these new errors.

## 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 fmt` and `zk lint`.

---------

Signed-off-by: Danil <[email protected]>
Co-authored-by: Danil <[email protected]>
Co-authored-by: Stanislav Breadless <[email protected]>
  • Loading branch information
3 people authored Oct 15, 2024
1 parent c1cb30e commit c60a348
Show file tree
Hide file tree
Showing 59 changed files with 1,856 additions and 452 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-core-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
ci_run git config --global --add safe.directory /usr/src/zksync/contracts
ci_run ./bin/zk || true
ci_run run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key
- name: Install zkstack
run: |
ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup || true
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/new-build-contract-verifier-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,13 @@ jobs:
crate: sqlx-cli
tag: 0.8.1

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@8f1998e9878d786675189ef566a2e4bf24869773 # v1.2.0
- name: Install foundry-zksync
run: |
mkdir ./foundry-zksync
curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz
tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync
chmod +x ./foundry-zksync/forge ./foundry-zksync/cast
echo "$PWD/foundry-zksync" >> $GITHUB_PATH
- name: Pre-download compilers
shell: bash
Expand All @@ -131,7 +136,7 @@ jobs:
docker compose up -d postgres
- name: Install zkstack
run: |
run: |
./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup
zkstackup --local || true
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/new-build-core-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ jobs:
crate: sqlx-cli
tag: 0.8.1

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@8f1998e9878d786675189ef566a2e4bf24869773 # v1.2.0
- name: Install foundry-zksync
run: |
mkdir ./foundry-zksync
curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz
tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync
chmod +x ./foundry-zksync/forge ./foundry-zksync/cast
echo "$PWD/foundry-zksync" >> $GITHUB_PATH
- name: Pre-download compilers
shell: bash
Expand All @@ -136,10 +141,10 @@ jobs:
docker compose up -d postgres
- name: Install zkstack
run: |
run: |
./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup
zkstackup --local || true
- name: build contracts
shell: bash
run: |
Expand Down
2 changes: 1 addition & 1 deletion contracts
Submodule contracts updated 333 files
13 changes: 10 additions & 3 deletions core/bin/system-constants-generator/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc};
use once_cell::sync::Lazy;
use zksync_contracts::{
load_sys_contract, read_bootloader_code, read_bytecode_from_path, read_sys_contract_bytecode,
BaseSystemContracts, ContractLanguage, SystemContractCode,
read_zbin_bytecode, BaseSystemContracts, ContractLanguage, SystemContractCode,
};
use zksync_multivm::{
interface::{
Expand Down Expand Up @@ -171,9 +171,16 @@ pub(super) fn get_l1_txs(number_of_txs: usize) -> (Vec<Transaction>, Vec<Transac
}

fn read_bootloader_test_code(test: &str) -> Vec<u8> {
read_bytecode_from_path(format!(
if let Some(contract) = read_bytecode_from_path(format!(
"contracts/system-contracts/zkout/{test}.yul/contracts-preprocessed/bootloader/{test}.yul.json",
))
)){
contract
} else {
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/tests/artifacts/{}.yul.zbin",
test
))
}
}

fn default_l1_batch() -> L1BatchEnv {
Expand Down
7 changes: 5 additions & 2 deletions core/lib/basic_types/src/protocol_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,16 @@ pub enum ProtocolVersionId {
Version23,
Version24,
Version25,
Version26,
}

impl ProtocolVersionId {
pub const fn latest() -> Self {
Self::Version24
Self::Version25
}

pub const fn next() -> Self {
Self::Version25
Self::Version26
}

pub fn try_from_packed_semver(packed_semver: U256) -> Result<Self, String> {
Expand Down Expand Up @@ -120,6 +121,7 @@ impl ProtocolVersionId {
ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory,
ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
}
}

Expand Down Expand Up @@ -275,6 +277,7 @@ impl From<ProtocolVersionId> for VmVersion {
ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory,
ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory,
}
}
}
Expand Down
133 changes: 96 additions & 37 deletions core/lib/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,21 @@ fn home_path() -> PathBuf {
Workspace::locate().core()
}

fn read_file_to_json_value(path: impl AsRef<Path> + std::fmt::Debug) -> serde_json::Value {
fn read_file_to_json_value(path: impl AsRef<Path> + std::fmt::Debug) -> Option<serde_json::Value> {
let zksync_home = home_path();
let path = Path::new(&zksync_home).join(path);
let file =
File::open(&path).unwrap_or_else(|e| panic!("Failed to open file {:?}: {}", path, e));
serde_json::from_reader(BufReader::new(file))
.unwrap_or_else(|e| panic!("Failed to parse file {:?}: {}", path, e))
let file = File::open(&path).ok()?;
Some(
serde_json::from_reader(BufReader::new(file))
.unwrap_or_else(|e| panic!("Failed to parse file {:?}: {}", path, e)),
)
}

fn load_contract_if_present<P: AsRef<Path> + std::fmt::Debug>(path: P) -> Option<Contract> {
let zksync_home = home_path();
let path = Path::new(&zksync_home).join(path);
path.exists().then(|| {
serde_json::from_value(read_file_to_json_value(&path)["abi"].take())
serde_json::from_value(read_file_to_json_value(&path).unwrap()["abi"].take())
.unwrap_or_else(|e| panic!("Failed to parse contract abi from file {:?}: {}", path, e))
})
}
Expand Down Expand Up @@ -114,17 +115,26 @@ pub fn load_contract<P: AsRef<Path> + std::fmt::Debug>(path: P) -> Contract {
}

pub fn load_sys_contract(contract_name: &str) -> Contract {
load_contract(format!(
if let Some(contract) = load_contract_if_present(format!(
"contracts/system-contracts/artifacts-zk/contracts-preprocessed/{0}.sol/{0}.json",
contract_name
))
)) {
contract
} else {
load_contract(format!(
"contracts/system-contracts/zkout/{0}.sol/{0}.json",
contract_name
))
}
}

pub fn read_contract_abi(path: impl AsRef<Path> + std::fmt::Debug) -> String {
read_file_to_json_value(path)["abi"]
.as_str()
.expect("Failed to parse abi")
.to_string()
pub fn read_contract_abi(path: impl AsRef<Path> + std::fmt::Debug) -> Option<String> {
Some(
read_file_to_json_value(path)?["abi"]
.as_str()
.expect("Failed to parse abi")
.to_string(),
)
}

pub fn bridgehub_contract() -> Contract {
Expand Down Expand Up @@ -200,7 +210,7 @@ pub fn l1_messenger_contract() -> Contract {

/// Reads bytecode from the path RELATIVE to the Cargo workspace location.
pub fn read_bytecode(relative_path: impl AsRef<Path> + std::fmt::Debug) -> Vec<u8> {
read_bytecode_from_path(relative_path)
read_bytecode_from_path(relative_path).expect("Exists")
}

pub fn eth_contract() -> Contract {
Expand All @@ -212,25 +222,33 @@ pub fn known_codes_contract() -> Contract {
}

/// Reads bytecode from a given path.
pub fn read_bytecode_from_path(artifact_path: impl AsRef<Path> + std::fmt::Debug) -> Vec<u8> {
let artifact = read_file_to_json_value(&artifact_path);

let bytecode = artifact["bytecode"]
.as_str()
.unwrap_or_else(|| panic!("Bytecode not found in {:?}", artifact_path))
.strip_prefix("0x")
.unwrap_or_else(|| panic!("Bytecode in {:?} is not hex", artifact_path));
pub fn read_bytecode_from_path(
artifact_path: impl AsRef<Path> + std::fmt::Debug,
) -> Option<Vec<u8>> {
let artifact = read_file_to_json_value(&artifact_path)?;

let bytecode = if let Some(bytecode) = artifact["bytecode"].as_str() {
bytecode
.strip_prefix("0x")
.unwrap_or_else(|| panic!("Bytecode in {:?} is not hex", artifact_path))
} else {
artifact["bytecode"]["object"]
.as_str()
.unwrap_or_else(|| panic!("Bytecode not found in {:?}", artifact_path))
};

hex::decode(bytecode)
.unwrap_or_else(|err| panic!("Can't decode bytecode in {:?}: {}", artifact_path, err))
Some(
hex::decode(bytecode)
.unwrap_or_else(|err| panic!("Can't decode bytecode in {:?}: {}", artifact_path, err)),
)
}

pub fn read_sys_contract_bytecode(directory: &str, name: &str, lang: ContractLanguage) -> Vec<u8> {
DEFAULT_SYSTEM_CONTRACTS_REPO.read_sys_contract_bytecode(directory, name, lang)
}

static DEFAULT_SYSTEM_CONTRACTS_REPO: Lazy<SystemContractsRepo> =
Lazy::new(SystemContractsRepo::from_env);
Lazy::new(SystemContractsRepo::default);

/// Structure representing a system contract repository - that allows
/// fetching contracts that are located there.
Expand All @@ -240,38 +258,65 @@ pub struct SystemContractsRepo {
pub root: PathBuf,
}

impl SystemContractsRepo {
impl Default for SystemContractsRepo {
/// Returns the default system contracts repository with directory based on the Cargo workspace location.
pub fn from_env() -> Self {
fn default() -> Self {
SystemContractsRepo {
root: home_path().join("contracts/system-contracts"),
}
}
}

impl SystemContractsRepo {
pub fn read_sys_contract_bytecode(
&self,
directory: &str,
name: &str,
lang: ContractLanguage,
) -> Vec<u8> {
match lang {
ContractLanguage::Sol => read_bytecode_from_path(self.root.join(format!(
"artifacts-zk/contracts-preprocessed/{0}{1}.sol/{1}.json",
directory, name
))),
ContractLanguage::Sol => {
if let Some(contracts) = read_bytecode_from_path(
self.root
.join(format!("zkout/{0}{1}.sol/{1}.json", directory, name)),
) {
contracts
} else {
read_bytecode_from_path(self.root.join(format!(
"artifacts-zk/contracts-preprocessed/{0}{1}.sol/{1}.json",
directory, name
)))
.expect("One of the outputs should exists")
}
}
ContractLanguage::Yul => {
let artifacts_path = self
.root
.join(format!("contracts-preprocessed/{}artifacts/", directory));
read_yul_bytecode_by_path(artifacts_path, name)
if let Some(contract) = read_bytecode_from_path(self.root.join(format!(
"zkout/{name}.yul/contracts-preprocessed/{directory}/{name}.yul.json",
))) {
contract
} else {
read_zbin_bytecode_from_path(self.root.join(format!(
"contracts-preprocessed/{0}artifacts/{1}.yul.zbin",
directory, name
)))
}
}
}
}
}

pub fn read_bootloader_code(bootloader_type: &str) -> Vec<u8> {
let artifacts_path = "contracts/system-contracts/bootloader/build/artifacts/";
read_yul_bytecode(artifacts_path, bootloader_type)
if let Some(contract) =
read_bytecode_from_path(home_path().join("contracts/system-contracts").join(format!(
"zkout/{bootloader_type}.yul/contracts-preprocessed/bootloader/{bootloader_type}.yul.json",
)))
{
return contract;
};
read_zbin_bytecode(format!(
"contracts/system-contracts/bootloader/build/artifacts/{}.yul.zbin",
bootloader_type
))
}

fn read_proved_batch_bootloader_bytecode() -> Vec<u8> {
Expand Down Expand Up @@ -463,6 +508,13 @@ impl BaseSystemContracts {
BaseSystemContracts::load_with_bootloader(bootloader_bytecode)
}

pub fn playground_post_protocol_defense() -> Self {
let bootloader_bytecode = read_zbin_bytecode(
"etc/multivm_bootloaders/vm_protocol_defense/playground_batch.yul/playground_batch.yul.zbin",
);
BaseSystemContracts::load_with_bootloader(bootloader_bytecode)
}

pub fn estimate_gas_pre_virtual_blocks() -> Self {
let bootloader_bytecode = read_zbin_bytecode(
"etc/multivm_bootloaders/vm_1_3_2/fee_estimate.yul/fee_estimate.yul.zbin",
Expand Down Expand Up @@ -526,6 +578,13 @@ impl BaseSystemContracts {
BaseSystemContracts::load_with_bootloader(bootloader_bytecode)
}

pub fn estimate_gas_post_protocol_defense() -> Self {
let bootloader_bytecode = read_zbin_bytecode(
"etc/multivm_bootloaders/vm_protocol_defense/fee_estimate.yul/fee_estimate.yul.zbin",
);
BaseSystemContracts::load_with_bootloader(bootloader_bytecode)
}

pub fn hashes(&self) -> BaseSystemContractsHashes {
BaseSystemContractsHashes {
bootloader: self.bootloader.hash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,8 @@ fn test_l1_tx_execution() {
let res = vm.vm.execute(VmExecutionMode::OneTx);
let storage_logs = res.logs.storage_logs;
let res = StorageWritesDeduplicator::apply_on_empty_state(&storage_logs);
// We changed one slot inside contract. However, the rewrite of the `basePubdataSpent` didn't happen, since it was the same
// as the start of the previous tx. Thus we have `+1` slot for the changed counter and `-1` slot for base pubdata spent
assert_eq!(res.initial_storage_writes, basic_initial_writes);
// We changed one slot inside contract.
assert_eq!(res.initial_storage_writes - basic_initial_writes, 1);

// No repeated writes
let repeated_writes = res.repeated_storage_writes;
Expand All @@ -146,7 +145,7 @@ fn test_l1_tx_execution() {
assert!(result.result.is_failed(), "The transaction should fail");

let res = StorageWritesDeduplicator::apply_on_empty_state(&result.logs.storage_logs);
assert_eq!(res.initial_storage_writes, basic_initial_writes);
assert_eq!(res.initial_storage_writes, basic_initial_writes + 1);
assert_eq!(res.repeated_storage_writes, 1);
}

Expand Down
Loading

0 comments on commit c60a348

Please sign in to comment.