mavryk_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 mavryk_crypto_rs as crypto;
13
14use crypto::hash::ContractKt1Hash;
15use crypto::hash::HashType;
16use crypto::hash::SmartRollupHash;
17use mavryk_data_encoding::enc::BinWriter;
18use mavryk_smart_rollup_core::PREIMAGE_HASH_SIZE;
19use mavryk_smart_rollup_encoding::inbox;
20use mavryk_smart_rollup_encoding::michelson::Michelson;
21use mavryk_smart_rollup_encoding::michelson::MichelsonUnit;
22use mavryk_smart_rollup_encoding::public_key_hash::PublicKeyHash;
23use mavryk_smart_rollup_encoding::smart_rollup::SmartRollupAddress;
24use mavryk_smart_rollup_encoding::timestamp::Timestamp;
25use mavryk_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
47pub use state::InMemoryStore;
48
49#[derive(Debug)]
51pub struct MockHost {
52 state: RefCell<HostState>,
53 info: inbox::InfoPerLevel,
54}
55
56impl Default for MockHost {
57 fn default() -> Self {
58 let address = SmartRollupAddress::new(SmartRollupHash(vec![
59 0;
60 HashType::SmartRollupHash
61 .size()
62 ]));
63
64 Self::with_address(&address)
65 }
66}
67
68pub struct TransferMetadata {
73 sender: ContractKt1Hash,
74 source: PublicKeyHash,
75 destination: Option<SmartRollupAddress>,
76}
77
78impl TransferMetadata {
79 pub fn new<E1: std::fmt::Debug, E2: std::fmt::Debug>(
81 sender: impl TryInto<ContractKt1Hash, Error = E1>,
82 source: impl TryInto<PublicKeyHash, Error = E2>,
83 ) -> Self {
84 Self {
85 sender: sender.try_into().unwrap(),
86 source: source.try_into().unwrap(),
87 destination: None,
88 }
89 }
90
91 pub fn override_destination(&mut self, dest: SmartRollupAddress) {
94 self.destination = Some(dest);
95 }
96}
97
98impl MockHost {
99 pub fn with_address(address: &SmartRollupAddress) -> Self {
101 let raw_rollup_address = address
102 .hash()
103 .0
104 .as_slice()
105 .try_into()
106 .expect("Incorrect length for SmartRollupHash");
107
108 let mut state = HostState::default_with_metadata(RollupMetadata {
109 raw_rollup_address,
110 origination_level: NAIROBI_ACTIVATION_LEVEL,
111 });
112
113 let reboots = MAXIMUM_REBOOTS_PER_INPUT;
116 let bytes = reboots.to_le_bytes().to_vec();
117 state.store.0.set_value(REBOOT_COUNTER_KEY, bytes);
118
119 state.curr_level = NAIROBI_ACTIVATION_LEVEL;
120
121 let info = info_for_level(state.curr_level as i32);
122
123 let mut host = Self {
124 state: state.into(),
125 info,
126 };
127
128 host.bump_level();
130
131 host
132 }
133
134 pub fn add_transfer(&mut self, payload: impl Michelson, metadata: &TransferMetadata) {
136 let destination = metadata.destination.clone().unwrap_or_else(|| {
137 SmartRollupAddress::new(self.state.borrow().get_metadata().address())
138 });
139
140 let transfer = inbox::Transfer {
141 payload,
142 sender: metadata.sender.clone(),
143 source: metadata.source.clone(),
144 destination,
145 };
146
147 let message = inbox::InboxMessage::Internal(
148 inbox::InternalInboxMessage::Transfer(transfer),
149 );
150
151 let mut inbox_message = Vec::new();
152 message
153 .serialize(&mut inbox_message)
154 .expect("serialization of transfer failed");
155
156 self.as_mut().add_input(inbox_message);
157 }
158
159 pub fn add_external(&mut self, message: impl BinWriter) {
161 let mut payload = Vec::new();
162 message
163 .bin_write(&mut payload)
164 .expect("serialization of external payload failed");
165
166 let external_message = inbox::InboxMessage::External::<MichelsonUnit>(&payload);
167
168 self.add_inbox_message(external_message);
169 }
170
171 pub fn set_preimage(&mut self, preimage: Vec<u8>) -> [u8; PREIMAGE_HASH_SIZE] {
173 self.as_mut().set_preimage(preimage)
174 }
175
176 pub fn run_level(&mut self, kernel_run: fn(&mut Self)) -> u32 {
181 self.finalise_inputs();
182
183 let mut reboots = MAXIMUM_REBOOTS_PER_INPUT;
184
185 loop {
186 let bytes = reboots.to_le_bytes().to_vec();
187 self.as_mut().store.0.set_value(REBOOT_COUNTER_KEY, bytes);
188
189 kernel_run(self);
190 self.as_mut().store.0.node_delete(TOO_MANY_REBOOT_FLAG_KEY);
191
192 reboots -= 1;
193
194 let reboot_requested = self
195 .as_mut()
196 .store
197 .0
198 .maybe_get_value(REBOOT_FLAG_KEY)
199 .is_some();
200
201 if reboot_requested {
202 self.as_mut().store.0.node_delete(REBOOT_FLAG_KEY);
203
204 if reboots > 0 {
205 continue;
206 }
207
208 self.as_mut()
209 .store
210 .0
211 .set_value(TOO_MANY_REBOOT_FLAG_KEY, vec![]);
212 }
213 break;
214 }
215
216 let level_ran_at = self.state.borrow().curr_level;
217 self.bump_level();
218
219 level_ran_at
220 }
221
222 pub fn level(&self) -> u32 {
224 self.state.borrow().curr_level
225 }
226
227 pub fn info_per_level(&self) -> &inbox::InfoPerLevel {
229 &self.info
230 }
231
232 pub fn outbox_at(&self, level: u32) -> Vec<Vec<u8>> {
234 self.state.borrow().store.0.outbox_at(level).to_vec()
235 }
236
237 fn bump_level(&mut self) {
238 let state = self.as_mut();
239 state.curr_level += 1;
240 state.curr_input_id = 0;
241 state.input.truncate(0);
242 let curr_info = info_for_level(state.curr_level as i32);
243
244 let sol = inbox::InboxMessage::<MichelsonUnit>::Internal(
245 inbox::InternalInboxMessage::StartOfLevel,
246 );
247
248 self.add_inbox_message(sol);
249
250 let info = inbox::InboxMessage::<MichelsonUnit>::Internal(
251 inbox::InternalInboxMessage::InfoPerLevel(curr_info.clone()),
252 );
253
254 self.add_inbox_message(info);
255 self.info = curr_info;
256 }
257
258 fn finalise_inputs(&mut self) {
259 let eol = inbox::InboxMessage::<MichelsonUnit>::Internal(
260 inbox::InternalInboxMessage::EndOfLevel,
261 );
262
263 self.add_inbox_message(eol);
264 }
265
266 fn add_inbox_message<Expr: Michelson>(&mut self, message: inbox::InboxMessage<Expr>) {
267 let mut inbox_message = Vec::new();
268 message
269 .serialize(&mut inbox_message)
270 .expect("serialization of message failed");
271
272 self.as_mut().add_input(inbox_message);
273 }
274}
275
276fn info_for_level(level: i32) -> inbox::InfoPerLevel {
277 let timestamp = (level as i64 - 1 - (NAIROBI_ACTIVATION_LEVEL as i64))
278 * NAIROBI_BLOCK_TIME
279 + NAIROBI_ACTIVATION_TIMESTAMP;
280
281 let hash = crypto::blake2b::digest_256(×tamp.to_le_bytes()).unwrap();
282
283 inbox::InfoPerLevel {
284 predecessor: crypto::hash::BlockHash(hash),
285 predecessor_timestamp: Timestamp::from(timestamp),
286 }
287}