Skip to content
16 changes: 12 additions & 4 deletions docs/system_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ System hooks have two distinct use cases:
- Implementing system functionality needed for ZKsync operations:
- L1 messenger system hook
- Set bytecode on address system hook
- Mint base token system hook (temporary, to be replaced)
- Contract deployer system hook (temporary for backward compatibility)
- Mint base token system hook (used only for system-level mints)

## L1 messenger system hook

Expand All @@ -26,11 +27,13 @@ It can only be called by the L1 messenger system contract at address `0x8008`.
The input should be the ABI-encoded parameters: sender address and message bytes.

Implementation of the L1 messenger system hook decodes the input and records the message using the system method.
Calls from any other caller are treated as calls to an empty account: success with empty returndata and no side effects.

## Set bytecode on address system hook

The set bytecode on address system hook (at address `0x7002`) allows setting deployed EVM bytecode to any address.
It can only be called by the Contract Deployer system contract at address `0x8006`.
It can only be called by the Contract Deployer system contract at address `0x8006`
or directly by the ComplexUpgrader system contract at address `0x800f`.

The hook accepts the following ABI-encoded parameters:
- `address` - target address to set bytecode on (32 bytes, ABI padded)
Expand All @@ -43,6 +46,7 @@ Key features:
- Used exclusively for protocol upgrades approved by governance
- Does not publish full bytecode in pubdata to fit within gas/calldata limits
- Bytecodes are published separately via Ethereum calldata
- Calls from unauthorized callers are treated as calls to an empty account: success with empty returndata, no writes, and no EVM gas burn.

## Mint base token system hook

Expand All @@ -52,12 +56,16 @@ The calldata must be exactly 32 bytes containing the amount to mint (as uint256)

## Contract deployer system hook

The contract deployer system hook implements only 1 method: `setBytecodeDetailsEVM(address,bytes32,uint32,bytes32)`.
It allows setting any deployed EVM bytecode to any address but can be called only by the special system address.
This hook is temporary needed for backward compatibility to not break existing upgrade flow.

The contract deployer system hook is installed on the ContractDeployer address `0x8006`.
It implements only 1 method: `setBytecodeDetailsEVM(address,bytes32,uint32,bytes32)`.
It allows setting any deployed EVM bytecode to any address, but only when called by the ComplexUpgrader system address `0x800f`.

It accepts bytecode hash, bytecode length, and observable bytecode hash.
Please note that full bytecode will not be published in the pubdata.
We want to be able to perform upgrade with 1 tx, so we designed this method this way (by hashes + without pubdata) to fit into gas/calldata/pubdata limits.

It will be used only by protocol upgrade transactions, which are approved by governance.
Bytecodes will be published separately with Ethereum calldata.
Calls from unauthorized callers are treated as calls to an empty account: success with empty returndata, no writes, and no EVM gas burn.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//!
//! Contract deployer system hook implementation.
//! It implements a `setDeployedCodeEVM` method, similar to Era.
//! It implements a `setBytecodeDetailsEVM` method, similar to Era.
//! It's needed for protocol upgrades.
//!
use super::super::*;
Expand All @@ -13,7 +13,7 @@ use zk_ee::system_log;
use zk_ee::utils::Bytes32;
use zk_ee::{internal_error, out_of_return_memory};

pub fn contract_deployer_hook<'a, S: EthereumLikeTypes>(
pub fn contract_deployer_temp_hook<'a, S: EthereumLikeTypes>(
request: ExternalCallRequest<S>,
caller_ee: u8,
system: &mut System<S>,
Expand All @@ -36,6 +36,18 @@ where

debug_assert_eq!(callee, CONTRACT_DEPLOYER_ADDRESS);

if caller != COMPLEX_UPGRADER_ADDRESS {
system_log!(
system,
"ContractDeployer hook: invalid caller (caller={caller:?})\n"
);
// Pretend to be an empty account
return Ok((
make_return_state_from_returndata_region(available_resources, &[]),
return_memory,
));
}

// There are no "payable" methods
let mut error = nominal_token_value != U256::ZERO;
let mut is_static = false;
Expand Down Expand Up @@ -99,7 +111,6 @@ where

// setBytecodeDetailsEVM(address,bytes32,uint32,bytes32) - f6eca0b0
pub const SET_EVM_BYTECODE_DETAILS: &[u8] = &[0xf6, 0xec, 0xa0, 0xb0];
pub const L2_COMPLEX_UPGRADER_ADDRESS: B160 = B160::from_limbs([0x800f, 0, 0]);

fn contract_deployer_hook_inner<S: EthereumLikeTypes>(
mut calldata: &[u8],
Expand Down Expand Up @@ -133,8 +144,8 @@ where
"Contract deployer failure: setBytecodeDetailsEVM called with static context",
));
}
// in future we need to handle regular(not genesis) protocol upgrades
if caller != L2_COMPLEX_UPGRADER_ADDRESS {
// Additional security check just in case. Call from anauthorized caller should be filtered earlier.
if caller != COMPLEX_UPGRADER_ADDRESS {
return Ok(Err(
"Contract deployer failure: unauthorized caller for setBytecodeDetailsEVM",
));
Expand Down Expand Up @@ -174,7 +185,7 @@ where

// Although this can be called as a part of protocol upgrade,
// we are checking the next invariants, just in case
// EIP-158: reject code of length > 24576.
// EIP-170: reject code of length > 24576.
if bytecode_length as usize > MAX_CODE_SIZE {
return Ok(Err(
"Contract deployer failure: setBytecodeDetailsEVM called with invalid bytecode(length > 24576)",
Expand Down
2 changes: 1 addition & 1 deletion system_hooks/src/call_hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(feature = "mock-unsupported-precompiles")]
pub mod mock_precompiles;

pub mod contract_deployer;
pub mod contract_deployer_temp;
pub mod l1_messenger;
pub mod precompiles;
pub mod set_bytecode_on_address;
Expand Down
4 changes: 2 additions & 2 deletions system_hooks/src/call_hooks/set_bytecode_on_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ where

debug_assert_eq!(callee, SET_BYTECODE_ON_ADDRESS_HOOK);

// Can be used only by Contract Deployer system contract
if caller != CONTRACT_DEPLOYER_ADDRESS {
// Can be used only by Contract Deployer system contract or directly by complex upgrader
if caller != CONTRACT_DEPLOYER_ADDRESS && caller != COMPLEX_UPGRADER_ADDRESS {
system_log!(
system,
"Set bytecode hook: invalid caller (caller={caller:?})\n"
Expand Down
4 changes: 2 additions & 2 deletions system_hooks/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
extern crate alloc;

use crate::addresses_constants::*;
use crate::call_hooks::contract_deployer::contract_deployer_hook;
use crate::call_hooks::contract_deployer_temp::contract_deployer_temp_hook;
use crate::call_hooks::l1_messenger::l1_messenger_hook;
use crate::call_hooks::mint_base_token::mint_base_token_hook;
use crate::call_hooks::set_bytecode_on_address::set_bytecode_on_address_hook;
Expand All @@ -49,7 +49,7 @@
system::{
base_system_functions::{
Bn254AddErrors, Bn254MulErrors, Bn254PairingCheckErrors, ModExpErrors,
P256VerifyErrors, RipeMd160Errors, Secp256k1ECRecoverErrors, Sha256Errors,

Check warning on line 52 in system_hooks/src/lib.rs

View workflow job for this annotation

GitHub Actions / eth_stf_run

unused import: `P256VerifyErrors`

Check warning on line 52 in system_hooks/src/lib.rs

View workflow job for this annotation

GitHub Actions / Run benchmarks

unused import: `P256VerifyErrors`

Check warning on line 52 in system_hooks/src/lib.rs

View workflow job for this annotation

GitHub Actions / Run benchmarks

unused import: `P256VerifyErrors`

Check warning on line 52 in system_hooks/src/lib.rs

View workflow job for this annotation

GitHub Actions / Run benchmarks

unused import: `P256VerifyErrors`

Check warning on line 52 in system_hooks/src/lib.rs

View workflow job for this annotation

GitHub Actions / Run benchmarks

unused import: `P256VerifyErrors`
},
errors::subsystem::Subsystem,
EthereumLikeTypes, System, SystemTypes, *,
Expand Down Expand Up @@ -232,7 +232,7 @@
{
hooks.add_call_hook(
CONTRACT_DEPLOYER_ADDRESS_LOW,
SystemCallHook::new(contract_deployer_hook),
SystemCallHook::new(contract_deployer_temp_hook),
)
}

Expand Down
Loading
Loading