anvil_zksync_core/deps/
system_contracts.rs

1use anvil_zksync_config::types::SystemContractsOptions;
2use flate2::read::GzDecoder;
3use once_cell::sync::Lazy;
4use serde_json::Value;
5use std::collections::HashMap;
6use std::io::Read;
7use std::path::Path;
8use zksync_types::system_contracts::{
9    get_system_smart_contracts, get_system_smart_contracts_from_dir,
10};
11use zksync_types::{
12    block::DeployedContract, ProtocolVersionId, ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS,
13    BOOTLOADER_UTILITIES_ADDRESS, CODE_ORACLE_ADDRESS, COMPLEX_UPGRADER_ADDRESS,
14    COMPRESSOR_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, CREATE2_FACTORY_ADDRESS,
15    ECRECOVER_PRECOMPILE_ADDRESS, EC_ADD_PRECOMPILE_ADDRESS, EC_MUL_PRECOMPILE_ADDRESS,
16    EC_PAIRING_PRECOMPILE_ADDRESS, EVENT_WRITER_ADDRESS, EVM_GAS_MANAGER_ADDRESS,
17    EVM_HASHES_STORAGE_ADDRESS, EVM_PREDEPLOYS_MANAGER_ADDRESS, IDENTITY_ADDRESS,
18    IMMUTABLE_SIMULATOR_STORAGE_ADDRESS, KECCAK256_PRECOMPILE_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS,
19    L1_MESSENGER_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_BASE_TOKEN_ADDRESS, L2_BRIDGEHUB_ADDRESS,
20    L2_GENESIS_UPGRADE_ADDRESS, L2_MESSAGE_ROOT_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS,
21    L2_WRAPPED_BASE_TOKEN_IMPL, MODEXP_PRECOMPILE_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS,
22    NONCE_HOLDER_ADDRESS, PUBDATA_CHUNK_PUBLISHER_ADDRESS, SECP256R1_VERIFY_PRECOMPILE_ADDRESS,
23    SHA256_PRECOMPILE_ADDRESS, SLOAD_CONTRACT_ADDRESS, SYSTEM_CONTEXT_ADDRESS,
24};
25use zksync_types::{AccountTreeId, Address, H160};
26
27pub const TIMESTAMP_ASSERTER_ADDRESS: Address = H160([
28    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29    0x00, 0x80, 0x80, 0x12,
30]);
31
32static BUILTIN_CONTRACT_ARCHIVES: [(ProtocolVersionId, &[u8]); 3] = [
33    (
34        ProtocolVersionId::Version26,
35        include_bytes!("contracts/builtin-contracts-v26.tar.gz"),
36    ),
37    (
38        ProtocolVersionId::Version27,
39        include_bytes!("contracts/builtin-contracts-v27.tar.gz"),
40    ),
41    (
42        ProtocolVersionId::Version28,
43        include_bytes!("contracts/builtin-contracts-v28.tar.gz"),
44    ),
45];
46
47static BUILTIN_CONTRACT_ARTIFACTS: Lazy<HashMap<ProtocolVersionId, HashMap<String, Vec<u8>>>> =
48    Lazy::new(|| {
49        let mut result = HashMap::new();
50        for (protocol_version, built_in_contracts) in BUILTIN_CONTRACT_ARCHIVES {
51            let decoder = GzDecoder::new(built_in_contracts);
52            let mut archive = tar::Archive::new(decoder);
53            let mut contract_artifacts = HashMap::new();
54            for file in archive
55                .entries()
56                .expect("failed to decompress built-in contracts")
57            {
58                let mut file = file.expect("failed to read a built-in contract entry");
59                let path = file
60                    .header()
61                    .path()
62                    .expect("contract path is malformed")
63                    .file_name()
64                    .expect("built-in contract entry does not have a filename")
65                    .to_string_lossy()
66                    .to_string();
67
68                let mut contents = Vec::with_capacity(
69                    file.header().size().expect("contract size is corrupted") as usize,
70                );
71                file.read_to_end(&mut contents).unwrap();
72                contract_artifacts.insert(path, contents);
73            }
74            result.insert(protocol_version, contract_artifacts);
75        }
76        result
77    });
78
79pub fn bytecode_from_slice(artifact_name: &str, contents: &[u8]) -> Vec<u8> {
80    let artifact: Value = serde_json::from_slice(contents).expect(artifact_name);
81    let bytecode = artifact["bytecode"]
82        .as_object()
83        .unwrap_or_else(|| panic!("Bytecode not found in {:?}", artifact_name))
84        .get("object")
85        .unwrap_or_else(|| panic!("Bytecode object not found in {:?}", artifact_name))
86        .as_str()
87        .unwrap_or_else(|| panic!("Bytecode object is not a string in {:?}", artifact_name));
88
89    hex::decode(bytecode)
90        .unwrap_or_else(|err| panic!("Can't decode bytecode in {:?}: {}", artifact_name, err))
91}
92
93pub fn load_builtin_contract(protocol_version: ProtocolVersionId, artifact_name: &str) -> Vec<u8> {
94    let artifact_path = format!("{artifact_name}.json");
95    bytecode_from_slice(
96        artifact_name,
97        BUILTIN_CONTRACT_ARTIFACTS
98            .get(&protocol_version)
99            .unwrap_or_else(|| panic!("protocol version '{protocol_version}' is not supported"))
100            .get(&artifact_path)
101            .unwrap_or_else(|| {
102                panic!("failed to find built-in contract artifact at '{artifact_path}'")
103            }),
104    )
105}
106
107/// Build a static map of “everything” (kernel + non-kernel + precompile + L2 + empty).
108static BUILTIN_CONTRACTS: Lazy<HashMap<ProtocolVersionId, Vec<DeployedContract>>> =
109    Lazy::new(|| {
110        let mut result = HashMap::new();
111
112        for (protocol_version, _) in BUILTIN_CONTRACT_ARCHIVES {
113            let mut list = Vec::new();
114
115            // (1) Kernel
116            list.extend(
117                BUILTIN_CONTRACT_LOCATIONS
118                    .iter()
119                    .filter(|(_, _, min_version)| &protocol_version >= min_version)
120                    .map(|(artifact_name, address, _)| DeployedContract {
121                        account_id: AccountTreeId::new(*address),
122                        bytecode: load_builtin_contract(protocol_version, artifact_name),
123                    }),
124            );
125
126            list.extend(
127                NON_KERNEL_CONTRACT_LOCATIONS
128                    .iter()
129                    .filter(|(_, _, min_version)| &protocol_version >= min_version)
130                    .map(|(artifact_name, address, _)| DeployedContract {
131                        account_id: AccountTreeId::new(*address),
132                        bytecode: load_builtin_contract(protocol_version, artifact_name),
133                    }),
134            );
135
136            result.insert(protocol_version, list);
137        }
138
139        result
140    });
141
142const V26: ProtocolVersionId = ProtocolVersionId::Version26;
143const V27: ProtocolVersionId = ProtocolVersionId::Version27;
144const V28: ProtocolVersionId = ProtocolVersionId::Version28;
145
146/// Triple containing a name of a contract, its L2 address and minimum supported protocol version
147static BUILTIN_CONTRACT_LOCATIONS: [(&str, Address, ProtocolVersionId); 30] = [
148    // *************************************************
149    // *     Kernel contracts (base offset 0x8000)     *
150    // *************************************************
151    ("AccountCodeStorage", ACCOUNT_CODE_STORAGE_ADDRESS, V26),
152    ("NonceHolder", NONCE_HOLDER_ADDRESS, V26),
153    ("KnownCodesStorage", KNOWN_CODES_STORAGE_ADDRESS, V26),
154    (
155        "ImmutableSimulator",
156        IMMUTABLE_SIMULATOR_STORAGE_ADDRESS,
157        V26,
158    ),
159    ("ContractDeployer", CONTRACT_DEPLOYER_ADDRESS, V26),
160    ("L1Messenger", L1_MESSENGER_ADDRESS, V26),
161    ("MsgValueSimulator", MSG_VALUE_SIMULATOR_ADDRESS, V26),
162    ("L2BaseToken", L2_BASE_TOKEN_ADDRESS, V26),
163    ("SystemContext", SYSTEM_CONTEXT_ADDRESS, V26),
164    ("BootloaderUtilities", BOOTLOADER_UTILITIES_ADDRESS, V26),
165    ("EventWriter", EVENT_WRITER_ADDRESS, V26),
166    ("Compressor", COMPRESSOR_ADDRESS, V26),
167    ("ComplexUpgrader", COMPLEX_UPGRADER_ADDRESS, V26),
168    (
169        "PubdataChunkPublisher",
170        PUBDATA_CHUNK_PUBLISHER_ADDRESS,
171        V26,
172    ),
173    ("EvmGasManager", EVM_GAS_MANAGER_ADDRESS, V27),
174    ("EvmPredeploysManager", EVM_PREDEPLOYS_MANAGER_ADDRESS, V27),
175    ("EvmHashesStorage", EVM_HASHES_STORAGE_ADDRESS, V27),
176    // *************************************************
177    // *                 Precompiles                   *
178    // *************************************************
179    ("Keccak256", KECCAK256_PRECOMPILE_ADDRESS, V26),
180    ("SHA256", SHA256_PRECOMPILE_ADDRESS, V26),
181    ("Ecrecover", ECRECOVER_PRECOMPILE_ADDRESS, V26),
182    ("EcAdd", EC_ADD_PRECOMPILE_ADDRESS, V26),
183    ("EcMul", EC_MUL_PRECOMPILE_ADDRESS, V26),
184    ("EcPairing", EC_PAIRING_PRECOMPILE_ADDRESS, V26),
185    ("CodeOracle", CODE_ORACLE_ADDRESS, V26),
186    ("P256Verify", SECP256R1_VERIFY_PRECOMPILE_ADDRESS, V26),
187    ("Identity", IDENTITY_ADDRESS, V27),
188    ("Modexp", MODEXP_PRECOMPILE_ADDRESS, V28),
189    // TODO: It might make more sense to source address for these from zkstack config
190    // *************************************************
191    // *                L2 contracts                   *
192    // *************************************************
193    ("TimestampAsserter", TIMESTAMP_ASSERTER_ADDRESS, V26),
194    // *************************************************
195    // *               Empty contracts                 *
196    // *************************************************
197    // For now, only zero address and the bootloader address have empty bytecode at the init
198    // In the future, we might want to set all of the system contracts this way.
199    ("EmptyContract", Address::zero(), V26),
200    ("EmptyContract", BOOTLOADER_ADDRESS, V26),
201];
202
203/// *************************************************************
204/// *  Non-kernel contracts (base offset 0x010000)             *
205/// *************************************************************
206pub static NON_KERNEL_CONTRACT_LOCATIONS: [(&str, Address, ProtocolVersionId); 8] = [
207    ("Create2Factory", CREATE2_FACTORY_ADDRESS, V26),
208    ("L2GenesisUpgrade", L2_GENESIS_UPGRADE_ADDRESS, V26),
209    ("Bridgehub", L2_BRIDGEHUB_ADDRESS, V26),
210    ("L2AssetRouter", L2_ASSET_ROUTER_ADDRESS, V26),
211    ("L2NativeTokenVault", L2_NATIVE_TOKEN_VAULT_ADDRESS, V26),
212    ("MessageRoot", L2_MESSAGE_ROOT_ADDRESS, V26),
213    ("SloadContract", SLOAD_CONTRACT_ADDRESS, V26),
214    ("L2WrappedBaseToken", L2_WRAPPED_BASE_TOKEN_IMPL, V26),
215];
216
217pub fn get_deployed_contracts(
218    options: SystemContractsOptions,
219    protocol_version: ProtocolVersionId,
220    system_contracts_path: Option<&Path>,
221) -> Vec<DeployedContract> {
222    match options {
223        SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
224            BUILTIN_CONTRACTS
225                .get(&protocol_version)
226                .unwrap_or_else(|| panic!("protocol version '{protocol_version}' is not supported"))
227                .clone()
228        }
229        SystemContractsOptions::Local => {
230            // checks if system contracts path is provided
231            if let Some(path) = system_contracts_path {
232                get_system_smart_contracts_from_dir(path.to_path_buf())
233            } else {
234                get_system_smart_contracts()
235            }
236        }
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    fn count_protocol_contracts(protocol_version: ProtocolVersionId) -> usize {
245        let kernel_count = BUILTIN_CONTRACT_LOCATIONS
246            .iter()
247            .filter(|(_, _, min_version)| &protocol_version >= min_version)
248            .count();
249
250        let non_kernel_count = NON_KERNEL_CONTRACT_LOCATIONS
251            .iter()
252            .filter(|(_, _, min_version)| &protocol_version >= min_version)
253            .count();
254
255        kernel_count + non_kernel_count
256    }
257
258    #[test]
259    fn load_v26_contracts() {
260        let contracts = get_deployed_contracts(
261            SystemContractsOptions::BuiltIn,
262            ProtocolVersionId::Version26,
263            None,
264        );
265        assert_eq!(
266            contracts.len(),
267            count_protocol_contracts(ProtocolVersionId::Version26)
268        );
269    }
270
271    #[test]
272    fn load_v27_contracts() {
273        let contracts = get_deployed_contracts(
274            SystemContractsOptions::BuiltIn,
275            ProtocolVersionId::Version27,
276            None,
277        );
278        assert_eq!(
279            contracts.len(),
280            count_protocol_contracts(ProtocolVersionId::Version27)
281        );
282    }
283
284    #[test]
285    fn load_v28_contracts() {
286        let contracts = get_deployed_contracts(
287            SystemContractsOptions::BuiltIn,
288            ProtocolVersionId::Version28,
289            None,
290        );
291        assert_eq!(
292            contracts.len(),
293            count_protocol_contracts(ProtocolVersionId::Version28)
294        );
295    }
296}