1use std::{
4 collections::{hash_map::DefaultHasher, BTreeMap},
5 hash::{Hash, Hasher},
6};
7
8use primitive_types::{H160, U256};
9use zkevm_opcode_defs::{
10 ethereum_types::Address, system_params::DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW,
11};
12use zksync_vm2_interface::Tracer;
13
14use crate::{
15 instruction_handlers::address_into_u256, Program, StorageInterface, StorageSlot, World,
16};
17
18#[derive(Debug)]
20pub struct TestWorld<T> {
21 pub(crate) address_to_hash: BTreeMap<U256, U256>,
22 pub(crate) hash_to_contract: BTreeMap<U256, Program<T, Self>>,
23}
24
25impl<T: Tracer> TestWorld<T> {
26 pub fn new(contracts: &[(Address, Program<T, Self>)]) -> Self {
32 let mut address_to_hash = BTreeMap::new();
33 let mut hash_to_contract = BTreeMap::new();
34 for (i, (address, code)) in contracts.iter().enumerate() {
35 let mut hasher = DefaultHasher::new();
37 i.hash(&mut hasher);
38 code.code_page().hash(&mut hasher);
39
40 let mut code_info_bytes = [0; 32];
41 code_info_bytes[24..].copy_from_slice(&hasher.finish().to_be_bytes());
42 let code_len = u16::try_from(code.code_page().len())
43 .expect("code length must not exceed u16::MAX");
44 code_info_bytes[2..=3].copy_from_slice(&code_len.to_be_bytes());
45 code_info_bytes[0] = 1;
46 let hash = U256::from_big_endian(&code_info_bytes);
47
48 address_to_hash.insert(address_into_u256(*address), hash);
49 hash_to_contract.insert(hash, code.clone());
50 }
51 Self {
52 address_to_hash,
53 hash_to_contract,
54 }
55 }
56}
57
58impl<T: Tracer> World<T> for TestWorld<T> {
59 fn decommit(&mut self, hash: U256) -> Program<T, Self> {
60 if let Some(program) = self.hash_to_contract.get(&hash) {
61 program.clone()
62 } else {
63 panic!("unexpected decommit")
64 }
65 }
66
67 fn decommit_code(&mut self, hash: U256) -> Vec<u8> {
68 self.decommit(hash)
69 .code_page()
70 .iter()
71 .flat_map(|u256| {
72 let mut buffer = [0u8; 32];
73 u256.to_big_endian(&mut buffer);
74 buffer
75 })
76 .collect()
77 }
78}
79
80impl<T> StorageInterface for TestWorld<T> {
81 fn read_storage(&mut self, contract: H160, key: U256) -> StorageSlot {
82 let deployer_system_contract_address =
83 Address::from_low_u64_be(DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW.into());
84
85 if contract == deployer_system_contract_address {
86 let value = self
87 .address_to_hash
88 .get(&key)
89 .copied()
90 .unwrap_or_else(U256::zero);
91 StorageSlot {
92 value,
93 is_write_initial: false,
94 }
95 } else {
96 StorageSlot::EMPTY
97 }
98 }
99
100 fn cost_of_writing_storage(&mut self, _initial_slot: StorageSlot, _new_value: U256) -> u32 {
101 50
102 }
103
104 fn is_free_storage_slot(&self, _contract: &H160, _key: &U256) -> bool {
105 false
106 }
107}
108
109#[doc(hidden)] pub fn initial_decommit<T: Tracer, W: World<T>>(world: &mut W, address: H160) -> Program<T, W> {
114 let deployer_system_contract_address =
115 Address::from_low_u64_be(DEPLOYER_SYSTEM_CONTRACT_ADDRESS_LOW.into());
116 let code_info =
117 world.read_storage_value(deployer_system_contract_address, address_into_u256(address));
118
119 let mut code_info_bytes = [0; 32];
120 code_info.to_big_endian(&mut code_info_bytes);
121
122 code_info_bytes[1] = 0;
123 let code_key: U256 = U256::from_big_endian(&code_info_bytes);
124
125 world.decommit(code_key)
126}