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}