arbiter_core/environment/mod.rs
1//! The [`environment`] module provides abstractions and functionality for
2//! handling the Ethereum execution environment. This includes managing its
3//! state, interfacing with the EVM, and broadcasting events to subscribers.
4//! Other features include the ability to control block rate and gas settings
5//! and execute other database modifications from external agents.
6//!
7//! The key integration for the environment is the Rust EVM [`revm`](https://github.com/bluealloy/revm).
8//! This is an implementation of the EVM in Rust that we utilize for processing
9//! raw smart contract bytecode.
10//!
11//! Core structures:
12//! - [`Environment`]: Represents the Ethereum execution environment, allowing
13//! for its management (e.g., starting, stopping) and interfacing with agents.
14//! - [`EnvironmentParameters`]: Parameters necessary for creating or modifying
15//! an [`Environment`].
16//! - [`Instruction`]: Enum indicating the type of instruction that is being
17//! sent
18//! to the EVM.
19
20use std::thread::{self, JoinHandle};
21
22use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
23use ethers::{abi::AbiDecode, types::ValueOrArray};
24use revm::{
25 db::AccountState,
26 inspector_handle_register,
27 primitives::{Env, HashMap, B256},
28};
29use tokio::sync::broadcast::channel;
30
31use super::*;
32#[cfg_attr(doc, doc(hidden))]
33#[cfg_attr(doc, allow(unused_imports))]
34#[cfg(doc)]
35use crate::middleware::ArbiterMiddleware;
36use crate::{
37 console::abi::HardhatConsoleCalls, database::inspector::ArbiterInspector,
38 middleware::connection::revm_logs_to_ethers_logs,
39};
40
41pub mod instruction;
42use instruction::*;
43
44/// Alias for the sender of the channel for transmitting transactions.
45pub(crate) type InstructionSender = Sender<Instruction>;
46
47/// Alias for the receiver of the channel for transmitting transactions.
48pub(crate) type InstructionReceiver = Receiver<Instruction>;
49
50/// Alias for the sender of the channel for transmitting [`RevmResult`] emitted
51/// from transactions.
52pub(crate) type OutcomeSender = Sender<Result<Outcome, ArbiterCoreError>>;
53
54/// Alias for the receiver of the channel for transmitting [`RevmResult`]
55/// emitted from transactions.
56pub(crate) type OutcomeReceiver = Receiver<Result<Outcome, ArbiterCoreError>>;
57
58/// Represents a sandboxed EVM environment.
59///
60/// ## Features
61/// * [`revm::Evm`] and its connections to the "outside world" (agents) via the
62/// [`Socket`] provide the [`Environment`] a means to route and execute
63/// transactions.
64/// * [`ArbiterDB`] is the database structure used that allows for read-only
65/// sharing of execution and write-only via the main thread. This can also be a
66/// database read in from disk storage via [`database::fork::Fork`].
67/// * [`ArbiterInspector`] is an that allows for the EVM to be able to display
68/// logs and properly handle gas payments.
69/// * [`EnvironmentParameters`] are used to set the gas limit, contract size
70/// limit, and label for the [`Environment`].
71#[derive(Debug)]
72pub struct Environment {
73 /// The label used to define the [`Environment`].
74 pub parameters: EnvironmentParameters,
75
76 /// The [`EVM`] that is used as an execution environment and database for
77 /// calls and transactions.
78 pub(crate) db: ArbiterDB,
79
80 inspector: Option<ArbiterInspector>,
81
82 /// This gives a means of letting the "outside world" connect to the
83 /// [`Environment`] so that users (or agents) may send and receive data from
84 /// the [`EVM`].
85 pub(crate) socket: Socket,
86
87 /// [`JoinHandle`] for the thread in which the [`EVM`] is running.
88 /// Used for assuring that the environment is stopped properly or for
89 /// performing any blocking action the end user needs.
90 pub(crate) handle: Option<JoinHandle<Result<(), ArbiterCoreError>>>,
91}
92
93/// Parameters to create [`Environment`]s with different settings.
94#[derive(Clone, Debug, Default, Deserialize, Serialize)]
95pub struct EnvironmentParameters {
96 /// The label used to define the [`Environment`].
97 pub label: Option<String>,
98
99 /// The gas limit for the blocks in the [`Environment`].
100 pub gas_limit: Option<U256>,
101
102 /// The contract size limit for the [`Environment`].
103 pub contract_size_limit: Option<usize>,
104
105 /// Enables inner contract logs to be printed to the console.
106 pub console_logs: bool,
107
108 /// Allows for turning off any gas payments for transactions so no inspector
109 /// is needed.
110 pub pay_gas: bool,
111}
112
113/// A builder for creating an [`Environment`].
114///
115/// This builder allows for the configuration of an [`Environment`] before it is
116/// instantiated. It provides methods for setting the label, gas limit, contract
117/// size limit, and a database for the [`Environment`].
118pub struct EnvironmentBuilder {
119 parameters: EnvironmentParameters,
120 db: ArbiterDB,
121}
122
123impl EnvironmentBuilder {
124 /// Builds and runs an [`Environment`] with the parameters set in the
125 /// [`EnvironmentBuilder`].
126 pub fn build(self) -> Environment {
127 Environment::create(self.parameters, self.db).run()
128 }
129
130 /// Sets the label for the [`Environment`].
131 pub fn with_label(mut self, label: impl Into<String>) -> Self {
132 self.parameters.label = Some(label.into());
133 self
134 }
135
136 /// Sets the gas limit for the [`Environment`].
137 pub fn with_gas_limit(mut self, gas_limit: U256) -> Self {
138 self.parameters.gas_limit = Some(gas_limit);
139 self
140 }
141
142 /// Sets the contract size limit for the [`Environment`].
143 pub fn with_contract_size_limit(mut self, contract_size_limit: usize) -> Self {
144 self.parameters.contract_size_limit = Some(contract_size_limit);
145 self
146 }
147
148 /// Sets the state for the [`Environment`]. This can come from a saved state
149 /// of a simulation or a [`database::fork::Fork`].
150 pub fn with_state(mut self, state: impl Into<CacheDB<EmptyDB>>) -> Self {
151 self.db.state = Arc::new(RwLock::new(state.into()));
152 self
153 }
154
155 /// Sets the logs for the [`Environment`]. This can come from a saved state
156 /// of a simulation and can be useful for doing analysis.
157 pub fn with_logs(
158 mut self,
159 logs: impl Into<std::collections::HashMap<U256, Vec<eLog>>>,
160 ) -> Self {
161 self.db.logs = Arc::new(RwLock::new(logs.into()));
162 self
163 }
164
165 /// Sets the entire database for the [`Environment`] including both the
166 /// state and logs. This can come from the saved state of a simulation and
167 /// can be useful for doing analysis.
168 pub fn with_arbiter_db(mut self, db: ArbiterDB) -> Self {
169 self.db = db;
170 self
171 }
172
173 /// Enables inner contract logs to be printed to the console as `trace`
174 /// level logs prepended with "Console logs: ".
175 pub fn with_console_logs(mut self) -> Self {
176 self.parameters.console_logs = true;
177 self
178 }
179
180 /// Turns on gas payments for transactions so that the [`EVM`] will
181 /// automatically pay for gas and revert if balance is not met by sender.
182 pub fn with_pay_gas(mut self) -> Self {
183 self.parameters.pay_gas = true;
184 self
185 }
186}
187
188impl Environment {
189 /// Creates a new [`EnvironmentBuilder`] with default parameters that can be
190 /// used to build an [`Environment`].
191 pub fn builder() -> EnvironmentBuilder {
192 EnvironmentBuilder {
193 parameters: EnvironmentParameters::default(),
194 db: ArbiterDB::default(),
195 }
196 }
197
198 fn create(parameters: EnvironmentParameters, db: ArbiterDB) -> Self {
199 let (instruction_sender, instruction_receiver) = unbounded();
200 let (event_broadcaster, _) = channel(512);
201 let socket = Socket {
202 instruction_sender: Arc::new(instruction_sender),
203 instruction_receiver,
204 event_broadcaster,
205 };
206
207 let inspector = if parameters.console_logs || parameters.pay_gas {
208 Some(ArbiterInspector::new(
209 parameters.console_logs,
210 parameters.pay_gas,
211 ))
212 } else {
213 Some(ArbiterInspector::new(false, false))
214 };
215
216 Self {
217 socket,
218 inspector,
219 parameters,
220 db,
221 handle: None,
222 }
223 }
224
225 /// This starts the [`Environment`] thread to process any [`Instruction`]s
226 /// coming through the [`Socket`].
227 fn run(mut self) -> Self {
228 // Bring in parameters for the `Environment`.
229 let label = self.parameters.label.clone();
230
231 // Bring in the EVM db and log storage by cloning the interior Arc
232 // (lightweight).
233 let db = self.db.clone();
234
235 // Bring in the EVM ENV
236 let mut env = Env::default();
237 env.cfg.limit_contract_code_size = self.parameters.contract_size_limit;
238 env.block.gas_limit = self.parameters.gas_limit.unwrap_or(U256::MAX);
239 // Bring in the inspector
240 let inspector = self.inspector.take().unwrap();
241
242 // Pull communication clones to move into a new thread.
243 let instruction_receiver = self.socket.instruction_receiver.clone();
244 let event_broadcaster = self.socket.event_broadcaster.clone();
245
246 // Move the EVM and its socket to a new thread and retrieve this handle
247 let handle = thread::spawn(move || {
248 // Create a new EVM builder
249 let mut evm = Evm::builder()
250 .with_db(db.clone())
251 .with_env(Box::new(env))
252 .with_external_context(inspector)
253 .append_handler_register(inspector_handle_register)
254 .build();
255
256 // Initialize counters that are returned on some receipts.
257 let mut transaction_index = U64::from(0_u64);
258 let mut cumulative_gas_per_block = eU256::from(0);
259
260 // Loop over the instructions sent through the socket.
261 while let Ok(instruction) = instruction_receiver.recv() {
262 trace!(
263 "Instruction {:?} received by environment labeled: {:?}",
264 instruction,
265 label
266 );
267 match instruction {
268 Instruction::AddAccount {
269 address,
270 outcome_sender,
271 } => {
272 let recast_address = Address::from(address.as_fixed_bytes());
273 let account = revm::db::DbAccount {
274 info: AccountInfo::default(),
275 account_state: AccountState::None,
276 storage: HashMap::new(),
277 };
278 match db.state.write()?.accounts.insert(recast_address, account) {
279 None => outcome_sender.send(Ok(Outcome::AddAccountCompleted))?,
280 Some(_) => {
281 outcome_sender.send(Err(ArbiterCoreError::AccountCreationError))?;
282 }
283 }
284 }
285 Instruction::BlockUpdate {
286 block_number,
287 block_timestamp,
288 outcome_sender,
289 } => {
290 // Return the old block data in a `ReceiptData`
291 let old_block_number = evm.block().number;
292 let receipt_data = ReceiptData {
293 block_number: convert_uint_to_u64(old_block_number)?,
294 transaction_index,
295 cumulative_gas_per_block,
296 };
297
298 // Update the block number and timestamp
299 evm.block_mut().number = U256::from_limbs(block_number.0);
300 evm.block_mut().timestamp = U256::from_limbs(block_timestamp.0);
301
302 // Reset the counters.
303 transaction_index = U64::from(0);
304 cumulative_gas_per_block = eU256::from(0);
305
306 // Return the old block data in a `ReceiptData` after the block update.
307 outcome_sender.send(Ok(Outcome::BlockUpdateCompleted(receipt_data)))?;
308 }
309 Instruction::Cheatcode {
310 cheatcode,
311 outcome_sender,
312 } => match cheatcode {
313 Cheatcodes::Load {
314 account,
315 key,
316 block: _,
317 } => {
318 let recast_address = Address::from(account.as_fixed_bytes());
319 let recast_key = B256::from(key.as_fixed_bytes()).into();
320
321 // Get the account storage value at the key in the db.
322 match db.state.write()?.accounts.get_mut(&recast_address) {
323 Some(account) => {
324 // Returns zero if the account is missing.
325 let value: U256 = match account.storage.get::<U256>(&recast_key)
326 {
327 Some(value) => *value,
328 None => U256::ZERO,
329 };
330 outcome_sender.send(Ok(Outcome::CheatcodeReturn(
331 CheatcodesReturn::Load { value },
332 )))?;
333 }
334 None => {
335 outcome_sender
336 .send(Err(ArbiterCoreError::AccountDoesNotExistError))?;
337 }
338 };
339 }
340 Cheatcodes::Store {
341 account,
342 key,
343 value,
344 } => {
345 let recast_address = Address::from(account.as_fixed_bytes());
346 let recast_key = B256::from(key.as_fixed_bytes());
347 let recast_value = B256::from(value.as_fixed_bytes());
348
349 // Mutate the db by inserting the new key-value pair into the account's
350 // storage and send the successful CheatcodeCompleted outcome.
351 match db.state.write()?.accounts.get_mut(&recast_address) {
352 Some(account) => {
353 account
354 .storage
355 .insert(recast_key.into(), recast_value.into());
356
357 outcome_sender.send(Ok(Outcome::CheatcodeReturn(
358 CheatcodesReturn::Store,
359 )))?;
360 }
361 None => {
362 outcome_sender
363 .send(Err(ArbiterCoreError::AccountDoesNotExistError))?;
364 }
365 };
366 }
367 Cheatcodes::Deal { address, amount } => {
368 let recast_address = Address::from(address.as_fixed_bytes());
369 match db.state.write()?.accounts.get_mut(&recast_address) {
370 Some(account) => {
371 account.info.balance += U256::from_limbs(amount.0);
372 outcome_sender.send(Ok(Outcome::CheatcodeReturn(
373 CheatcodesReturn::Deal,
374 )))?;
375 }
376 None => {
377 outcome_sender
378 .send(Err(ArbiterCoreError::AccountDoesNotExistError))?;
379 }
380 };
381 }
382 Cheatcodes::Access { address } => {
383 let recast_address = Address::from(address.as_fixed_bytes());
384 match db.state.write()?.accounts.get(&recast_address) {
385 Some(account) => {
386 let account_state = match account.account_state {
387 AccountState::None => AccountStateSerializable::None,
388 AccountState::Touched => AccountStateSerializable::Touched,
389 AccountState::StorageCleared => {
390 AccountStateSerializable::StorageCleared
391 }
392 AccountState::NotExisting => {
393 AccountStateSerializable::NotExisting
394 }
395 };
396
397 let account = CheatcodesReturn::Access {
398 account_state,
399 info: account.info.clone(),
400 storage: account.storage.clone(),
401 };
402
403 outcome_sender.send(Ok(Outcome::CheatcodeReturn(account)))?;
404 }
405 None => {
406 outcome_sender
407 .send(Err(ArbiterCoreError::AccountDoesNotExistError))?;
408 }
409 }
410 }
411 },
412 // A `Call` is not state changing and will not create events but will create
413 // console logs.
414 Instruction::Call {
415 tx_env,
416 outcome_sender,
417 } => {
418 // Set the tx_env and prepare to process it
419 *evm.tx_mut() = tx_env;
420
421 let result = evm.transact()?.result;
422
423 if let Some(console_log) = &mut evm.context.external.console_log {
424 console_log.0.drain(..).for_each(|log| {
425 // This unwrap is safe because the logs are guaranteed to be
426 // `HardhatConsoleCalls` by the `ArbiterInspector`.
427 trace!(
428 "Console logs: {:?}",
429 HardhatConsoleCalls::decode(log).unwrap().to_string()
430 )
431 });
432 };
433
434 outcome_sender.send(Ok(Outcome::CallCompleted(result)))?;
435 }
436 Instruction::SetGasPrice {
437 gas_price,
438 outcome_sender,
439 } => {
440 evm.tx_mut().gas_price = U256::from_limbs(gas_price.0);
441 outcome_sender.send(Ok(Outcome::SetGasPriceCompleted))?;
442 }
443
444 // A `Transaction` is state changing and will create events.
445 Instruction::Transaction {
446 tx_env,
447 outcome_sender,
448 } => {
449 // Set the tx_env and prepare to process it
450 *evm.tx_mut() = tx_env;
451
452 let execution_result = match evm.transact_commit() {
453 Ok(result) => {
454 if let Some(console_log) = &mut evm.context.external.console_log {
455 console_log.0.drain(..).for_each(|log| {
456 // This unwrap is safe because the logs are guaranteed to be
457 // `HardhatConsoleCalls` by the `ArbiterInspector`.
458 trace!(
459 "Console logs: {:?}",
460 HardhatConsoleCalls::decode(log).unwrap().to_string()
461 )
462 });
463 };
464 result
465 }
466 Err(e) => {
467 outcome_sender.send(Err(ArbiterCoreError::EVMError(e)))?;
468 continue;
469 }
470 };
471 cumulative_gas_per_block += eU256::from(execution_result.gas_used());
472 let block_number = convert_uint_to_u64(evm.block().number)?;
473 let receipt_data = ReceiptData {
474 block_number,
475 transaction_index,
476 cumulative_gas_per_block,
477 };
478
479 let mut logs = db.logs.write()?;
480 match logs.get_mut(&evm.block().number) {
481 Some(log_vec) => {
482 log_vec.extend(revm_logs_to_ethers_logs(
483 execution_result.logs().to_vec(),
484 &receipt_data,
485 ));
486 }
487 None => {
488 logs.insert(
489 evm.block().number,
490 revm_logs_to_ethers_logs(
491 execution_result.logs().to_vec(),
492 &receipt_data,
493 ),
494 );
495 }
496 }
497
498 match event_broadcaster.send(Broadcast::Event(
499 execution_result.logs().to_vec(),
500 receipt_data.clone(),
501 )) {
502 Ok(_) => {}
503 Err(_) => {
504 warn!(
505 "Event was not sent to any listeners. Are there any listeners?"
506 )
507 }
508 }
509 outcome_sender.send(Ok(Outcome::TransactionCompleted(
510 execution_result,
511 receipt_data,
512 )))?;
513
514 transaction_index += U64::from(1);
515 }
516 Instruction::Query {
517 environment_data,
518 outcome_sender,
519 } => {
520 let outcome = match environment_data {
521 EnvironmentData::BlockNumber => {
522 Ok(Outcome::QueryReturn(evm.block().number.to_string()))
523 }
524 EnvironmentData::BlockTimestamp => {
525 Ok(Outcome::QueryReturn(evm.block().timestamp.to_string()))
526 }
527 EnvironmentData::GasPrice => {
528 Ok(Outcome::QueryReturn(evm.tx().gas_price.to_string()))
529 }
530 EnvironmentData::Balance(address) => {
531 match db
532 .state
533 .read()
534 .unwrap()
535 .accounts
536 .get::<Address>(&address.as_fixed_bytes().into())
537 {
538 Some(account) => {
539 Ok(Outcome::QueryReturn(account.info.balance.to_string()))
540 }
541 None => Err(ArbiterCoreError::AccountDoesNotExistError),
542 }
543 }
544 EnvironmentData::TransactionCount(address) => {
545 match db
546 .state
547 .read()
548 .unwrap()
549 .accounts
550 .get::<Address>(&address.as_fixed_bytes().into())
551 {
552 Some(account) => {
553 Ok(Outcome::QueryReturn(account.info.nonce.to_string()))
554 }
555 None => Err(ArbiterCoreError::AccountDoesNotExistError),
556 }
557 }
558 EnvironmentData::Logs { filter } => {
559 let logs = db.logs.read().unwrap();
560 let from_block = U256::from(
561 filter
562 .block_option
563 .get_from_block()
564 .ok_or(ArbiterCoreError::MissingDataError)?
565 .as_number()
566 .ok_or(ArbiterCoreError::MissingDataError)?
567 .0[0],
568 );
569 let to_block = U256::from(
570 filter
571 .block_option
572 .get_to_block()
573 .ok_or(ArbiterCoreError::MissingDataError)?
574 .as_number()
575 .ok_or(ArbiterCoreError::MissingDataError)?
576 .0[0],
577 );
578 let mut return_logs = Vec::new();
579 logs.keys().for_each(|blocknum| {
580 if blocknum >= &from_block && blocknum <= &to_block {
581 return_logs.extend(logs.get(blocknum).cloned().unwrap());
582 }
583 });
584 return_logs.retain(|log| {
585 filter.topics.iter().any(|topic_option| match topic_option {
586 Some(topic_val_or_array) => match topic_val_or_array {
587 ValueOrArray::Value(topic) => match topic {
588 Some(topic) => log.topics.contains(topic),
589 None => true,
590 },
591 ValueOrArray::Array(topics) => {
592 topics.iter().any(|topic| match topic {
593 Some(topic) => log.topics.contains(topic),
594 None => true,
595 })
596 }
597 },
598 None => true,
599 })
600 });
601 return_logs.retain(|log| {
602 filter.address.iter().any(|address_value_or_array| {
603 match address_value_or_array {
604 ValueOrArray::Value(address) => &log.address == address,
605
606 ValueOrArray::Array(addresses) => {
607 addresses.iter().any(|addr| &log.address == addr)
608 }
609 }
610 })
611 });
612 Ok(Outcome::QueryReturn(
613 serde_json::to_string(&return_logs).unwrap(),
614 ))
615 }
616 };
617 outcome_sender.send(outcome)?;
618 }
619 Instruction::Stop(outcome_sender) => {
620 match event_broadcaster.send(Broadcast::StopSignal) {
621 Ok(_) => {}
622 Err(_) => {
623 warn!("Stop signal was not sent to any listeners. Are there any listeners?")
624 }
625 }
626 outcome_sender.send(Ok(Outcome::StopCompleted(db)))?;
627 break;
628 }
629 }
630 }
631 Ok(())
632 });
633 self.handle = Some(handle);
634 self
635 }
636
637 /// Stops the execution of the environment and returns the [`ArbiterDB`] in
638 /// its final state.
639 pub fn stop(mut self) -> Result<ArbiterDB, ArbiterCoreError> {
640 let (outcome_sender, outcome_receiver) = bounded(1);
641 self.socket
642 .instruction_sender
643 .send(Instruction::Stop(outcome_sender))?;
644 let outcome = outcome_receiver.recv()??;
645
646 let db = match outcome {
647 Outcome::StopCompleted(stopped_db) => stopped_db,
648 _ => unreachable!(),
649 };
650
651 if let Some(label) = &self.parameters.label {
652 warn!("Stopped environment with label: {}", label);
653 } else {
654 warn!("Stopped environment with no label.");
655 }
656 drop(self.socket.instruction_sender);
657 self.handle
658 .take()
659 .unwrap()
660 .join()
661 .map_err(|_| ArbiterCoreError::JoinError)??;
662 Ok(db)
663 }
664}
665
666/// Provides channels for communication between the EVM and external entities.
667///
668/// The socket contains senders and receivers for transactions, as well as an
669/// event broadcaster to broadcast logs from the EVM to subscribers.
670#[derive(Debug, Clone)]
671pub(crate) struct Socket {
672 pub(crate) instruction_sender: Arc<InstructionSender>,
673 pub(crate) instruction_receiver: InstructionReceiver,
674 pub(crate) event_broadcaster: BroadcastSender<Broadcast>,
675}
676
677/// Enum representing the types of broadcasts that can be sent.
678///
679/// This enum is used to differentiate between different types of broadcasts
680/// that can be sent from the environment to external entities.
681///
682/// Variants:
683/// * `StopSignal`: Represents a signal to stop the event logger process.
684/// * `Event(Vec<Log>)`: Represents a broadcast of a vector of Ethereum logs.
685#[derive(Clone, Debug)]
686pub enum Broadcast {
687 /// Represents a signal to stop the event logger process.
688 StopSignal,
689 /// Represents a broadcast of a vector of Ethereum logs.
690 Event(Vec<Log>, ReceiptData),
691}
692
693/// Convert a U256 to a U64, discarding the higher bits if the number is larger
694/// than 2^64 # Arguments
695/// * `input` - The U256 to convert.
696/// # Returns
697/// * `Ok(U64)` - The converted U64.
698/// Used for block number which is a U64.
699#[inline]
700fn convert_uint_to_u64(input: U256) -> Result<U64, ArbiterCoreError> {
701 let as_str = input.to_string();
702 match as_str.parse::<u64>() {
703 Ok(val) => Ok(val.into()),
704 Err(e) => Err(e)?,
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use super::*;
711
712 pub(crate) const TEST_ENV_LABEL: &str = "test";
713 const TEST_CONTRACT_SIZE_LIMIT: usize = 42069;
714 const TEST_GAS_LIMIT: u64 = 1_333_333_333_337;
715
716 #[test]
717 fn new_with_parameters() {
718 let environment = Environment::builder()
719 .with_label(TEST_ENV_LABEL)
720 .with_contract_size_limit(TEST_CONTRACT_SIZE_LIMIT)
721 .with_gas_limit(U256::from(TEST_GAS_LIMIT));
722 assert_eq!(environment.parameters.label, Some(TEST_ENV_LABEL.into()));
723 assert_eq!(
724 environment.parameters.contract_size_limit.unwrap(),
725 TEST_CONTRACT_SIZE_LIMIT
726 );
727 assert_eq!(
728 environment.parameters.gas_limit.unwrap(),
729 U256::from(TEST_GAS_LIMIT)
730 );
731 }
732
733 #[test]
734 fn conversion() {
735 // Test with a value that fits in u64.
736 let input = U256::from(10000);
737 assert_eq!(convert_uint_to_u64(input).unwrap(), U64::from(10000));
738
739 // Test with a value that is exactly at the limit of u64.
740 let input = U256::from(u64::MAX);
741 assert_eq!(convert_uint_to_u64(input).unwrap(), U64::from(u64::MAX));
742
743 // Test with a value that exceeds the limit of u64.
744 let input = U256::from(u64::MAX) + U256::from(1);
745 assert!(convert_uint_to_u64(input).is_err());
746 }
747}