tezos_smart_rollup_mock/
lib.rs1#![doc = include_str!("../README.md")]
6#![deny(missing_docs)]
7#![deny(rustdoc::broken_intra_doc_links)]
8
9mod host;
10mod state;
11
12extern crate tezos_crypto_rs as crypto;
13
14use crypto::hash::ContractKt1Hash;
15use crypto::hash::HashType;
16use crypto::hash::SmartRollupHash;
17use tezos_data_encoding::enc::BinWriter;
18use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE;
19use tezos_smart_rollup_encoding::inbox;
20use tezos_smart_rollup_encoding::michelson::Michelson;
21use tezos_smart_rollup_encoding::michelson::MichelsonUnit;
22use tezos_smart_rollup_encoding::public_key_hash::PublicKeyHash;
23use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress;
24use tezos_smart_rollup_encoding::timestamp::Timestamp;
25use tezos_smart_rollup_host::metadata::RollupMetadata;
26
27use state::HostState;
28use std::cell::RefCell;
29
30const MAXIMUM_REBOOTS_PER_INPUT: i32 = 1000;
31
32const REBOOT_FLAG_KEY: &str = "/kernel/env/reboot";
35
36const TOO_MANY_REBOOT_FLAG_KEY: &str = "/readonly/kernel/env/too_many_reboot";
37
38const REBOOT_COUNTER_KEY: &str = "/readonly/kernel/env/reboot_counter";
41
42const NAIROBI_ACTIVATION_LEVEL: u32 = 3_760_129;
43const NAIROBI_BLOCK_TIME: i64 = 15;
44const NAIROBI_ACTIVATION_TIMESTAMP: i64 = 1_687_561_630;
46
47#[derive(Debug)]
49pub struct MockHost {
50 state: RefCell<HostState>,
51 info: inbox::InfoPerLevel,
52}
53
54impl Default for MockHost {
55 fn default() -> Self {
56 let address = SmartRollupAddress::new(SmartRollupHash(vec![
57 0;
58 HashType::SmartRollupHash
59 .size()
60 ]));
61
62 Self::with_address(&address)
63 }
64}
65
66pub struct TransferMetadata {
71 sender: ContractKt1Hash,
72 source: PublicKeyHash,
73 destination: Option<SmartRollupAddress>,
74}
75
76impl TransferMetadata {
77 pub fn new<E1: std::fmt::Debug, E2: std::fmt::Debug>(
79 sender: impl TryInto<ContractKt1Hash, Error = E1>,
80 source: impl TryInto<PublicKeyHash, Error = E2>,
81 ) -> Self {
82 Self {
83 sender: sender.try_into().unwrap(),
84 source: source.try_into().unwrap(),
85 destination: None,
86 }
87 }
88
89 pub fn override_destination(&mut self, dest: SmartRollupAddress) {
92 self.destination = Some(dest);
93 }
94}
95
96impl MockHost {
97 pub fn with_address(address: &SmartRollupAddress) -> Self {
99 let raw_rollup_address = address
100 .hash()
101 .0
102 .as_slice()
103 .try_into()
104 .expect("Incorrect length for SmartRollupHash");
105
106 let mut state = HostState::default_with_metadata(RollupMetadata {
107 raw_rollup_address,
108 origination_level: NAIROBI_ACTIVATION_LEVEL,
109 });
110
111 let reboots = MAXIMUM_REBOOTS_PER_INPUT;
114 let bytes = reboots.to_le_bytes().to_vec();
115 state.store.set_value(REBOOT_COUNTER_KEY, bytes);
116
117 state.curr_level = NAIROBI_ACTIVATION_LEVEL;
118
119 let info = info_for_level(state.curr_level as i32);
120
121 Self {
122 state: state.into(),
123 info,
124 }
125 }
126
127 pub fn add_transfer(&mut self, payload: impl Michelson, metadata: &TransferMetadata) {
129 let destination = metadata.destination.clone().unwrap_or_else(|| {
130 SmartRollupAddress::new(self.state.borrow().get_metadata().address())
131 });
132
133 let transfer = inbox::Transfer {
134 payload,
135 sender: metadata.sender.clone(),
136 source: metadata.source.clone(),
137 destination,
138 };
139
140 let message = inbox::InboxMessage::Internal(
141 inbox::InternalInboxMessage::Transfer(transfer),
142 );
143
144 let mut inbox_message = Vec::new();
145 message
146 .serialize(&mut inbox_message)
147 .expect("serialization of transfer failed");
148
149 self.as_mut().add_input(inbox_message);
150 }
151
152 pub fn add_external(&mut self, message: impl BinWriter) {
154 let mut payload = Vec::new();
155 message
156 .bin_write(&mut payload)
157 .expect("serialization of external payload failed");
158
159 let external_message = inbox::InboxMessage::External::<MichelsonUnit>(&payload);
160
161 self.add_inbox_message(external_message);
162 }
163
164 pub fn set_preimage(&mut self, preimage: Vec<u8>) -> [u8; PREIMAGE_HASH_SIZE] {
166 self.as_mut().set_preimage(preimage)
167 }
168
169 pub fn run_level(&mut self, kernel_run: fn(&mut Self)) -> u32 {
174 self.finalise_inputs();
175
176 let mut reboots = MAXIMUM_REBOOTS_PER_INPUT;
177
178 loop {
179 let bytes = reboots.to_le_bytes().to_vec();
180 self.as_mut().store.set_value(REBOOT_COUNTER_KEY, bytes);
181
182 kernel_run(self);
183 self.as_mut().store.node_delete(TOO_MANY_REBOOT_FLAG_KEY);
184
185 reboots -= 1;
186
187 let reboot_requested = self
188 .as_mut()
189 .store
190 .maybe_get_value(REBOOT_FLAG_KEY)
191 .is_some();
192
193 if reboot_requested {
194 self.as_mut().store.node_delete(REBOOT_FLAG_KEY);
195
196 if reboots > 0 {
197 continue;
198 }
199
200 self.as_mut()
201 .store
202 .set_value(TOO_MANY_REBOOT_FLAG_KEY, vec![]);
203 }
204 break;
205 }
206
207 let level_ran_at = self.state.borrow().curr_level;
208 self.bump_level();
209
210 level_ran_at
211 }
212
213 pub fn level(&self) -> u32 {
215 self.state.borrow().curr_level
216 }
217
218 pub fn info_per_level(&self) -> &inbox::InfoPerLevel {
220 &self.info
221 }
222
223 pub fn outbox_at(&self, level: u32) -> Vec<Vec<u8>> {
225 self.state.borrow().store.outbox_at(level).to_vec()
226 }
227
228 fn bump_level(&mut self) {
229 let state = self.as_mut();
230 state.curr_level += 1;
231 state.curr_input_id = 0;
232 state.input.truncate(0);
233 let curr_info = info_for_level(state.curr_level as i32);
234
235 let sol = inbox::InboxMessage::<MichelsonUnit>::Internal(
236 inbox::InternalInboxMessage::StartOfLevel,
237 );
238
239 self.add_inbox_message(sol);
240
241 let info = inbox::InboxMessage::<MichelsonUnit>::Internal(
242 inbox::InternalInboxMessage::InfoPerLevel(curr_info.clone()),
243 );
244
245 self.add_inbox_message(info);
246 self.info = curr_info;
247 }
248
249 fn finalise_inputs(&mut self) {
250 let eol = inbox::InboxMessage::<MichelsonUnit>::Internal(
251 inbox::InternalInboxMessage::EndOfLevel,
252 );
253
254 self.add_inbox_message(eol);
255 }
256
257 fn add_inbox_message<Expr: Michelson>(&mut self, message: inbox::InboxMessage<Expr>) {
258 let mut inbox_message = Vec::new();
259 message
260 .serialize(&mut inbox_message)
261 .expect("serialization of message failed");
262
263 self.as_mut().add_input(inbox_message);
264 }
265}
266
267fn info_for_level(level: i32) -> inbox::InfoPerLevel {
268 let timestamp = (level as i64 - 1 - (NAIROBI_ACTIVATION_LEVEL as i64))
269 * NAIROBI_BLOCK_TIME
270 + NAIROBI_ACTIVATION_TIMESTAMP;
271
272 let hash = crypto::blake2b::digest_256(×tamp.to_le_bytes()).unwrap();
273
274 inbox::InfoPerLevel {
275 predecessor: crypto::hash::BlockHash(hash),
276 predecessor_timestamp: Timestamp::from(timestamp),
277 }
278}