concordium_smart_contract_testing/
impls.rs

1use crate::{
2    constants,
3    invocation::{ChangeSet, EntrypointInvocationHandler, TestConfigurationError},
4    types::*,
5    CONTRACT_MODULE_OUTPUT_PATH_ENV_VAR,
6};
7use anyhow::anyhow;
8use concordium_rust_sdk::{
9    self as sdk, base,
10    base::{
11        base::{AccountThreshold, Energy, InsufficientEnergy},
12        constants::MAX_WASM_MODULE_SIZE,
13        contracts_common::{
14            self, AccountAddress, AccountBalance, Address, Amount, ChainMetadata, ContractAddress,
15            Deserial, Duration, ExchangeRate, ExchangeRates, ModuleReference, OwnedPolicy,
16            ParseResult, SlotTime, Timestamp,
17        },
18        hashes::BlockHash,
19        smart_contracts::{ContractEvent, ModuleSource, WasmModule, WasmVersion},
20        transactions::{
21            self, cost, AccountAccessStructure, InitContractPayload, UpdateContractPayload,
22        },
23    },
24    smart_contracts::engine::{
25        v0,
26        v1::{self, DebugTracker, InvalidReturnCodeError, InvokeResponse},
27        wasm,
28        wasm::validate::ValidationConfig,
29        DebugInfo, InterpreterEnergy,
30    },
31    v2::Endpoint,
32};
33use num_bigint::BigUint;
34use num_integer::Integer;
35use sdk::{
36    smart_contracts::engine::wasm::CostConfigurationV1,
37    types::smart_contracts::InvokeContractResult,
38};
39use std::{
40    collections::{BTreeMap, BTreeSet},
41    env,
42    future::Future,
43    path::Path,
44    sync::Arc,
45};
46use tokio::{runtime, time::timeout};
47
48/// The timeout duration set for queries with an external node.
49const EXTERNAL_NODE_QUERY_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(10);
50
51/// The timeout duration set for connecting to an external node.
52const EXTERNAL_NODE_CONNECT_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(3);
53
54impl Default for Chain {
55    fn default() -> Self { Self::new() }
56}
57
58impl ChainParameters {
59    /// Create a new [`ChainParameters`](Self) where
60    ///  - `block_time` defaults to `0`,
61    ///  - `micro_ccd_per_euro` defaults to `50000 / 1`
62    ///  - `euro_per_energy` defaults to `1 / 50000`.
63    ///
64    /// With these exchange rates, one energy costs one microCCD.
65    pub fn new() -> Self {
66        Self::new_with_time_and_rates(
67            Timestamp::from_timestamp_millis(0),
68            ExchangeRate::new_unchecked(50000, 1),
69            ExchangeRate::new_unchecked(1, 50000),
70        )
71        .expect("Parameters are in range.")
72    }
73
74    /// Create a new [`ChainParameters`](Self) with a specified `block_time`
75    /// where
76    ///  - `micro_ccd_per_euro` defaults to `50000 / 1`
77    ///  - `euro_per_energy` defaults to `1 / 50000`.
78    pub fn new_with_time(block_time: SlotTime) -> Self {
79        Self {
80            block_time,
81            ..Self::new()
82        }
83    }
84
85    /// Create a new [`ChainParameters`](Self) where all the configurable
86    /// parameters are provided.
87    ///
88    /// Returns an error if the exchange rates provided makes one energy cost
89    /// more than `u64::MAX / 100_000_000_000`.
90    pub fn new_with_time_and_rates(
91        block_time: SlotTime,
92        micro_ccd_per_euro: ExchangeRate,
93        euro_per_energy: ExchangeRate,
94    ) -> Result<Self, ExchangeRateError> {
95        // Ensure the exchange rates are within a valid range.
96        check_exchange_rates(euro_per_energy, micro_ccd_per_euro)?;
97
98        Ok(Self {
99            block_time,
100            micro_ccd_per_euro,
101            euro_per_energy,
102        })
103    }
104
105    /// Helper function for converting [`Energy`] to [`Amount`] using the two
106    /// [`ExchangeRate`]s `euro_per_energy` and `micro_ccd_per_euro`.
107    pub fn calculate_energy_cost(&self, energy: Energy) -> Amount {
108        energy_to_amount(energy, self.euro_per_energy, self.micro_ccd_per_euro)
109    }
110}
111
112impl ChainBuilder {
113    /// Create a new [`ChainBuilder`] for constructing the [`Chain`].
114    ///
115    /// Can also be created via the [`Chain::builder`] method.
116    ///
117    /// To complete the building process, use [`ChainBuilder::build`], see the
118    /// example below.
119    ///
120    /// # Example
121    /// ```
122    /// # use concordium_smart_contract_testing::*;
123    ///
124    /// let chain = ChainBuilder::new()
125    ///             // Use zero or more builder methods, for example:
126    ///             .micro_ccd_per_euro(ExchangeRate::new_unchecked(50000, 1))
127    ///             .block_time(Timestamp::from_timestamp_millis(123))
128    ///             // Then build:
129    ///             .build()
130    ///             .unwrap();
131    /// ```
132    pub fn new() -> Self {
133        Self {
134            external_node_endpoint: None,
135            external_query_block: None,
136            micro_ccd_per_euro: None,
137            micro_ccd_per_euro_from_external: false,
138            euro_per_energy: None,
139            euro_per_energy_from_external: false,
140            block_time: None,
141            block_time_from_external: false,
142        }
143    }
144
145    /// Configure a connection to an external Concordium node.
146    ///
147    /// The connection can be used for getting the current exchange rates
148    /// between CCD, Euro and Energy.
149    ///
150    /// # Example
151    ///
152    /// ```no_run
153    /// # use concordium_smart_contract_testing::*;
154    /// let chain = Chain::builder()
155    ///     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
156    ///     .build()
157    ///     .unwrap();
158    /// ```
159    pub fn external_node_connection(mut self, endpoint: impl Into<Endpoint>) -> Self {
160        self.external_node_endpoint = Some(endpoint.into());
161        self
162    }
163
164    /// Configure the block to be used for all external queries.
165    ///
166    /// If this is not set, then the last final block will be queried during
167    /// [`ChainBuilder::build`] and saved, so it can be used for future queries.
168    ///
169    /// This can only be used in combination with
170    /// [`external_node_connection`][Self::external_node_connection].
171    ///
172    /// To view the configured block, see [`Chain::external_query_block`].
173    ///
174    /// # Example
175    ///
176    /// ```no_run
177    /// # use concordium_smart_contract_testing::*;
178    /// let chain = Chain::builder()
179    ///     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
180    ///     .external_query_block(
181    ///         "95ff82f26892a2327c3e7ac582224a54d75c367341fbff209bce552d81349eb0".parse().unwrap(),
182    ///     )
183    ///     .build()
184    ///     .unwrap();
185    /// ```
186    pub fn external_query_block(mut self, query_block: BlockHash) -> Self {
187        self.external_query_block = Some(query_block);
188        self
189    }
190
191    /// Configure the 'microCCD per euro' exchange rate.
192    ///
193    /// By default the rate is `50000 / 1`.
194    ///
195    /// This cannot be used together with
196    /// [`ChainBuilder::micro_ccd_per_euro_from_external`].
197    ///
198    /// # Example
199    /// ```
200    /// # use concordium_smart_contract_testing::*;
201    /// let chain = ChainBuilder::new()
202    ///     .micro_ccd_per_euro(ExchangeRate::new_unchecked(50000, 1))
203    ///     .build()
204    ///     .unwrap();
205    /// ```
206    pub fn micro_ccd_per_euro(mut self, exchange_rate: ExchangeRate) -> Self {
207        self.micro_ccd_per_euro = Some(exchange_rate);
208        self
209    }
210
211    /// Configure the 'euro per energy' exchange rate.
212    ///
213    /// By default the rate is `1 / 50000`.
214    ///
215    /// This cannot be used together with
216    /// [`ChainBuilder::euro_per_energy_from_external`].
217    ///
218    /// # Example
219    /// ```
220    /// # use concordium_smart_contract_testing::*;
221    /// let chain =
222    ///     ChainBuilder::new().euro_per_energy(ExchangeRate::new_unchecked(1, 50000)).build().unwrap();
223    /// ```
224    pub fn euro_per_energy(mut self, exchange_rate: ExchangeRate) -> Self {
225        self.euro_per_energy = Some(exchange_rate);
226        self
227    }
228
229    /// Configure the exchange rate between microCCD and euro using the external
230    /// node connection.
231    ///
232    /// This can only be used in combination with
233    /// [`external_node_connection`][Self::external_node_connection], and it
234    /// cannot be used together with
235    /// [`micro_ccd_per_euro`][Self::micro_ccd_per_euro].
236    ///
237    /// # Example
238    /// ```
239    /// # use concordium_smart_contract_testing::*;
240    /// let chain = ChainBuilder::new()
241    ///     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
242    ///     .micro_ccd_per_euro_from_external()
243    ///     .build()
244    ///     .unwrap();
245    /// ```
246    pub fn micro_ccd_per_euro_from_external(mut self) -> Self {
247        self.micro_ccd_per_euro_from_external = true;
248        self
249    }
250
251    /// Configure the exchange rate between euro and energy using the external
252    /// node connection.
253    ///
254    /// This can only be used in combination with
255    /// [`external_node_connection`][Self::external_node_connection], and it
256    /// cannot be used together with
257    /// [`euro_per_energy`][Self::euro_per_energy].
258    ///
259    /// # Example
260    /// ```
261    /// # use concordium_smart_contract_testing::*;
262    /// let chain = ChainBuilder::new()
263    ///     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
264    ///     .euro_per_energy_from_external()
265    ///     .build()
266    ///     .unwrap();
267    /// ```
268    pub fn euro_per_energy_from_external(mut self) -> Self {
269        self.euro_per_energy_from_external = true;
270        self
271    }
272
273    /// Configure the block time.
274    ///
275    /// By default the block time is `0`.
276    ///
277    /// This cannot be used in combination with
278    /// [`ChainBuilder::block_time_from_external`].
279    ///
280    /// # Example
281    /// ```
282    /// # use concordium_smart_contract_testing::*;
283    /// let chain = ChainBuilder::new()
284    ///     .block_time(Timestamp::from_timestamp_millis(1687440701000))
285    ///     .build()
286    ///     .unwrap();
287    /// ```
288    pub fn block_time(mut self, block_time: Timestamp) -> Self {
289        self.block_time = Some(block_time);
290        self
291    }
292
293    /// Configure the block time using the external node connection.
294    ///
295    /// This can only be used in combination with
296    /// [`external_node_connection`][Self::external_node_connection], and it
297    /// cannot be used together with
298    /// [`block_time`][Self::block_time].
299    ///
300    /// # Example
301    /// ```
302    /// # use concordium_smart_contract_testing::*;
303    /// let chain = ChainBuilder::new()
304    ///     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
305    ///     .block_time_from_external()
306    ///     .build()
307    ///     .unwrap();
308    /// ```
309    pub fn block_time_from_external(mut self) -> Self {
310        self.block_time_from_external = true;
311        self
312    }
313
314    /// Build the [`Chain`] with the configured options.
315    ///
316    /// # Example
317    /// ```
318    /// # use concordium_smart_contract_testing::*;
319    ///
320    /// let chain = Chain::builder()
321    ///             // Use zero or more builder methods, for example:
322    ///             .euro_per_energy(ExchangeRate::new_unchecked(1, 50000))
323    ///             .micro_ccd_per_euro(ExchangeRate::new_unchecked(50000, 1))
324    ///             // Then build:
325    ///             .build()
326    ///             .unwrap();
327    /// ```
328    pub fn build(self) -> Result<Chain, ChainBuilderError> {
329        // Create the chain with default parameters.
330        let mut chain = Chain::new();
331
332        // Setup the external node connection if provided. This also forwards and sets
333        // the external query block.
334        if let Some(endpoint) = self.external_node_endpoint {
335            chain.setup_external_node_connection(endpoint, self.external_query_block)?;
336        }
337
338        // Check for conflicting exchange rate configurations.
339        if self.micro_ccd_per_euro.is_some() && self.micro_ccd_per_euro_from_external {
340            return Err(ChainBuilderError::ConflictingMicroCCDPerEuro);
341        }
342        if self.euro_per_energy.is_some() && self.euro_per_energy_from_external {
343            return Err(ChainBuilderError::ConflictingEuroPerEnergy);
344        }
345
346        // Set the exchange rates via an external query.
347        if self.micro_ccd_per_euro_from_external || self.euro_per_energy_from_external {
348            let exchange_rates = chain.get_exchange_rates_via_external_node()?;
349            if self.micro_ccd_per_euro_from_external {
350                chain.parameters.micro_ccd_per_euro = exchange_rates.micro_ccd_per_euro;
351            }
352            if self.euro_per_energy_from_external {
353                chain.parameters.euro_per_energy = exchange_rates.euro_per_energy;
354            }
355        }
356
357        // Set the exchange rates directly.
358        if let Some(micro_ccd_per_euro) = self.micro_ccd_per_euro {
359            chain.parameters.micro_ccd_per_euro = micro_ccd_per_euro;
360        }
361        if let Some(euro_per_energy) = self.euro_per_energy {
362            chain.parameters.euro_per_energy = euro_per_energy;
363        }
364
365        // Check the exchange rates and return early if they are invalid.
366        check_exchange_rates(
367            chain.parameters.euro_per_energy,
368            chain.parameters.micro_ccd_per_euro,
369        )?;
370
371        match (self.block_time, self.block_time_from_external) {
372            (Some(_), true) => return Err(ChainBuilderError::ConflictingBlockTime),
373            (Some(block_time), false) => {
374                chain.parameters.block_time = block_time;
375            }
376            (None, true) => {
377                chain.set_block_time_via_external_node()?;
378            }
379            (None, false) => (),
380        }
381
382        // Replace the default block time if provided.
383        if let Some(block_time) = self.block_time {
384            chain.parameters.block_time = block_time;
385        }
386
387        Ok(chain)
388    }
389}
390
391impl Default for ChainBuilder {
392    fn default() -> Self { Self::new() }
393}
394
395// Exit early with an out of energy error.
396macro_rules! exit_ooe {
397    ($charge:expr, $trace:expr) => {
398        if let Err(InsufficientEnergy) = $charge {
399            return Err(ContractInitErrorKind::OutOfEnergy {
400                debug_trace: $trace,
401            });
402        }
403    };
404}
405
406impl Chain {
407    /// Get a [`ChainBuilder`] for constructing a new [`Chain`] with a builder
408    /// pattern.
409    ///
410    /// See the [`ChainBuilder`] for more details.
411    pub fn builder() -> ChainBuilder { ChainBuilder::new() }
412
413    /// Create a new [`Chain`](Self) where all the configurable parameters are
414    /// provided.
415    ///
416    /// Returns an error if the exchange rates provided makes one energy cost
417    /// more than `u64::MAX / 100_000_000_000`.
418    ///
419    /// *For more configuration options and flexibility, use the builder
420    /// pattern. See [`Chain::builder`].*
421    pub fn new_with_time_and_rates(
422        block_time: SlotTime,
423        micro_ccd_per_euro: ExchangeRate,
424        euro_per_energy: ExchangeRate,
425    ) -> Result<Self, ExchangeRateError> {
426        Ok(Self {
427            parameters:               ChainParameters::new_with_time_and_rates(
428                block_time,
429                micro_ccd_per_euro,
430                euro_per_energy,
431            )?,
432            accounts:                 BTreeMap::new(),
433            modules:                  BTreeMap::new(),
434            contracts:                BTreeMap::new(),
435            next_contract_index:      0,
436            external_node_connection: None,
437        })
438    }
439
440    /// Create a new [`Chain`](Self) with a specified `block_time` where
441    ///  - `micro_ccd_per_euro` defaults to `50000 / 1`
442    ///  - `euro_per_energy` defaults to `1 / 50000`.
443    ///
444    /// *For more configuration options and flexibility, use the builder
445    /// pattern. See [`Chain::builder`].*
446    pub fn new_with_time(block_time: SlotTime) -> Self {
447        Self {
448            parameters: ChainParameters::new_with_time(block_time),
449            ..Self::new()
450        }
451    }
452
453    /// Create a new [`Chain`](Self) where
454    ///  - `block_time` defaults to `0`,
455    ///  - `micro_ccd_per_euro` defaults to `50000 / 1`
456    ///  - `euro_per_energy` defaults to `1 / 50000`.
457    ///
458    /// With these exchange rates, one energy costs one microCCD.
459    ///
460    /// *For more configuration options and flexibility, use the builder
461    /// pattern. See [`Chain::builder`].*
462    pub fn new() -> Self {
463        Self::new_with_time_and_rates(
464            Timestamp::from_timestamp_millis(0),
465            ExchangeRate::new_unchecked(50000, 1),
466            ExchangeRate::new_unchecked(1, 50000),
467        )
468        .expect("Rates known to be within range.")
469    }
470
471    /// Helper function for converting [`Energy`] to [`Amount`] using the two
472    /// [`ExchangeRate`]s `euro_per_energy` and `micro_ccd_per_euro`.
473    pub fn calculate_energy_cost(&self, energy: Energy) -> Amount {
474        self.parameters.calculate_energy_cost(energy)
475    }
476
477    /// Get the state of the contract if it exists in the [`Chain`](Self).
478    pub fn get_contract(&self, address: ContractAddress) -> Option<&Contract> {
479        self.contracts.get(&address)
480    }
481
482    /// Get the the module if it exists in the [`Chain`](Self).
483    pub fn get_module(&self, module: ModuleReference) -> Option<&ContractModule> {
484        self.modules.get(&module)
485    }
486
487    /// Get the state of the account if it exists in the [`Chain`](Self).
488    /// Account addresses that are aliases will return the same account.
489    pub fn get_account(&self, address: AccountAddress) -> Option<&Account> {
490        self.accounts.get(&address.into())
491    }
492
493    /// Deploy a smart contract module using the same validation rules as
494    /// enforced by the node.
495    ///
496    /// The `WasmModule` can be loaded from disk with either
497    /// [`module_load_v1`] or [`module_load_v1_raw`].
498    ///
499    /// Parameters:
500    ///  - `signer`: the signer with a number of keys, which affects the cost.
501    ///  - `sender`: the sender account.
502    ///  - `module`: the v1 wasm module.
503    pub fn module_deploy_v1(
504        &mut self,
505        signer: Signer,
506        sender: AccountAddress,
507        wasm_module: WasmModule,
508    ) -> Result<ModuleDeploySuccess, ModuleDeployError> {
509        self.module_deploy_v1_debug(signer, sender, wasm_module, false)
510    }
511
512    /// Like [`module_deploy_v1`](Self::module_deploy_v1)
513    /// except that optionally debugging output may be allowed in the module.
514    pub fn module_deploy_v1_debug(
515        &mut self,
516        signer: Signer,
517        sender: AccountAddress,
518        wasm_module: WasmModule,
519        enable_debug: bool,
520    ) -> Result<ModuleDeploySuccess, ModuleDeployError> {
521        // For maintainers:
522        //
523        // This function does not correspond exactly to what happens in the node.
524        // There a user is also expected to give a max energy bound and the failures are
525        // slightly different. There it is possible to fail with "out of energy"
526        // error whereas here we only fail with "insufficient funds" if the user does
527        // not have enough CCD to pay.
528        //
529        // If users use our tools to deploy modules the costs are calculated for them so
530        // that deployment should never fail with out of energy. Not requiring energy
531        // provides a more ergonomic experience.
532        let Ok(sender_account) = self.accounts.get_mut(&sender.into()).ok_or(AccountDoesNotExist {
533            address: sender,
534        }) else {
535            // Ensure sender account exists.
536            return Err(ModuleDeployError {
537                kind:            ModuleDeployErrorKind::SenderDoesNotExist(AccountDoesNotExist {
538                    address: sender,
539                }),
540                energy_used:     0.into(),
541                transaction_fee: Amount::zero(),
542            });
543        };
544
545        // Only v1 modules are supported in this testing library.
546        // This error case does not exist in the node, so we don't need to match a
547        // specific cost. We charge 0 for it.
548        if wasm_module.version != WasmVersion::V1 {
549            return Err(ModuleDeployError {
550                kind:            ModuleDeployErrorKind::UnsupportedModuleVersion(
551                    wasm_module.version,
552                ),
553                energy_used:     0.into(),
554                transaction_fee: Amount::zero(),
555            });
556        }
557
558        let parameters = &self.parameters;
559        let check_header_energy = {
560            // +1 for the tag, +8 for size and version
561            let payload_size = 1
562                + 8
563                + wasm_module.source.size()
564                + transactions::construct::TRANSACTION_HEADER_SIZE;
565            cost::base_cost(payload_size, signer.num_keys)
566        };
567
568        // Calculate the deploy module cost.
569        let deploy_module_energy = cost::deploy_module(wasm_module.source.size());
570        let energy_used = check_header_energy + deploy_module_energy;
571        let transaction_fee = parameters.calculate_energy_cost(energy_used);
572
573        // Check if the account has sufficient balance to cover the transaction fee.
574        // This fee corresponds to the energy_reserved that our tools calculate when
575        // sending the transaction to the node. The account is not charged in the node
576        // unless it has sufficient balance to pay for the full deployment (and thus all
577        // the energy).
578        if sender_account.balance.available() < transaction_fee {
579            return Err(ModuleDeployError {
580                kind:            ModuleDeployErrorKind::InsufficientFunds,
581                energy_used:     0.into(),
582                transaction_fee: Amount::zero(),
583            });
584        };
585
586        // Charge the account.
587        sender_account.balance.total -= transaction_fee;
588
589        // Construct the artifact.
590        let artifact = match wasm::utils::instantiate_with_metering::<v1::ProcessedImports>(
591            ValidationConfig::V1,
592            CostConfigurationV1,
593            &v1::ConcordiumAllowedImports {
594                support_upgrade: true,
595                enable_debug,
596            },
597            wasm_module.source.as_ref(),
598        ) {
599            Ok(artifact) => artifact,
600            Err(err) => {
601                return Err(ModuleDeployError {
602                    kind: ModuleInvalidError(err).into(),
603                    energy_used,
604                    transaction_fee,
605                })
606            }
607        };
608
609        let module_reference: ModuleReference = wasm_module.get_module_ref();
610
611        // Ensure module hasn't been deployed before.
612        if self.modules.contains_key(&module_reference) {
613            return Err(ModuleDeployError {
614                kind: ModuleDeployErrorKind::DuplicateModule(module_reference),
615                energy_used,
616                transaction_fee,
617            });
618        }
619        self.modules.insert(module_reference, ContractModule {
620            // we follow protocol 6 semantics, and don't count the custom section size towards
621            // module size.
622            size:     wasm_module.source.size().saturating_sub(artifact.custom_sections_size),
623            artifact: Arc::new(artifact.artifact),
624        });
625        Ok(ModuleDeploySuccess {
626            module_reference,
627            energy_used,
628            transaction_fee,
629        })
630    }
631
632    /// Initialize a contract.
633    ///
634    /// **Parameters:**
635    ///  - `signer`: the signer with a number of keys, which affects the cost.
636    ///  - `sender`: The account paying for the transaction. Will also become
637    ///    the owner of the contract created.
638    ///  - `energy_reserved`: Amount of energy reserved for executing the init
639    ///    method.
640    ///  - `payload`:
641    ///    - `amount`: The initial balance of the contract. Subtracted from the
642    ///      `sender` account.
643    ///    - `mod_ref`: The reference to the a module that has already been
644    ///      deployed.
645    ///    - `init_name`: Name of the contract to initialize.
646    ///    - `param`: Parameter provided to the init method.
647    pub fn contract_init(
648        &mut self,
649        signer: Signer,
650        sender: AccountAddress,
651        energy_reserved: Energy,
652        payload: InitContractPayload,
653    ) -> Result<ContractInitSuccess, ContractInitError> {
654        let mut remaining_energy = energy_reserved;
655        if !self.account_exists(sender) {
656            return Err(self.convert_to_init_error(
657                ContractInitErrorKind::SenderDoesNotExist(AccountDoesNotExist {
658                    address: sender,
659                }),
660                energy_reserved,
661                remaining_energy,
662            ));
663        }
664
665        let res = self.contract_init_worker(
666            signer,
667            sender,
668            energy_reserved,
669            payload,
670            &mut remaining_energy,
671        );
672
673        let (res, transaction_fee) = match res {
674            Ok(s) => {
675                let transaction_fee = s.transaction_fee;
676                (Ok(s), transaction_fee)
677            }
678            Err(e) => {
679                let err = self.convert_to_init_error(e, energy_reserved, remaining_energy);
680                let transaction_fee = err.transaction_fee;
681                (Err(err), transaction_fee)
682            }
683        };
684
685        // Charge the account.
686        self.account_mut(sender).expect("existence already checked").balance.total -=
687            transaction_fee;
688        res
689    }
690
691    /// Helper method for initializing contracts, which does most of the actual
692    /// work.
693    ///
694    /// The main reason for splitting init in two is to have this method return
695    /// early if it runs out of energy. `contract_init` will then always
696    /// ensure to charge the account for the energy used.
697    fn contract_init_worker(
698        &mut self,
699        signer: Signer,
700        sender: AccountAddress,
701        energy_reserved: Energy,
702        payload: InitContractPayload,
703        remaining_energy: &mut Energy,
704    ) -> Result<ContractInitSuccess, ContractInitErrorKind> {
705        // Get the account and check that it has sufficient balance to pay for the
706        // reserved_energy and amount.
707        let account_info = self.account(sender)?;
708
709        let energy_reserved_cost = self.parameters.calculate_energy_cost(energy_reserved);
710
711        // Check that the account can pay for the reserved energy.
712        if account_info.balance.available() < energy_reserved_cost {
713            return Err(ContractInitErrorKind::InsufficientFunds);
714        }
715
716        // Compute the base cost for checking the transaction header.
717        let check_header_cost = {
718            // 1 byte for the tag.
719            let transaction_size =
720                transactions::construct::TRANSACTION_HEADER_SIZE + 1 + payload.size() as u64;
721            transactions::cost::base_cost(transaction_size, signer.num_keys)
722        };
723
724        // Charge the header cost.
725        exit_ooe!(remaining_energy.tick_energy(check_header_cost), DebugTracker::empty_trace());
726
727        // Ensure that the parameter has a valid size.
728        if payload.param.as_ref().len() > contracts_common::constants::MAX_PARAMETER_LEN {
729            return Err(ContractInitErrorKind::ParameterTooLarge);
730        }
731
732        // Charge the base cost for initializing a contract.
733        exit_ooe!(
734            remaining_energy.tick_energy(constants::INITIALIZE_CONTRACT_INSTANCE_BASE_COST),
735            DebugTracker::empty_trace()
736        );
737
738        // Check that the account also has enough funds to pay for the amount (in
739        // addition to the reserved energy).
740        if account_info.balance.available() < energy_reserved_cost + payload.amount {
741            return Err(ContractInitErrorKind::AmountTooLarge);
742        }
743
744        // Lookup module.
745        let module = self.contract_module(payload.mod_ref)?;
746        let lookup_cost = lookup_module_cost(&module);
747
748        // Charge the cost for looking up the module.
749        exit_ooe!(remaining_energy.tick_energy(lookup_cost), DebugTracker::empty_trace());
750
751        // Ensure the module contains the provided init name.
752        let init_name = payload.init_name.as_contract_name().get_chain_name();
753        if !module.artifact.export.contains_key(init_name) {
754            return Err(ContractInitErrorKind::ContractNotPresentInModule {
755                name: payload.init_name,
756            });
757        }
758
759        // Sender policies have a very bespoke serialization in
760        // order to allow skipping portions of them in smart contracts.
761        let sender_policies = {
762            let mut out = Vec::new();
763            account_info
764                .policy
765                .serial_for_smart_contract(&mut out)
766                .expect("Writing to a vector should succeed.");
767            out
768        };
769
770        // Construct the context.
771        let init_ctx = v0::InitContext {
772            metadata: ChainMetadata {
773                slot_time: self.parameters.block_time,
774            },
775            init_origin: sender,
776            sender_policies,
777        };
778        // Initialize contract
779        // We create an empty loader as no caching is used in this testing library
780        // presently, so the loader is not used.
781        let mut loader = v1::trie::Loader::new(&[][..]);
782
783        let energy_given_to_interpreter =
784            InterpreterEnergy::new(to_interpreter_energy(*remaining_energy));
785        let res = v1::invoke_init::<_, _, DebugTracker>(
786            module.artifact,
787            init_ctx,
788            v1::InitInvocation {
789                amount: payload.amount,
790                init_name,
791                parameter: payload.param.as_ref(),
792                energy: energy_given_to_interpreter,
793            },
794            false, // We only support protocol P5 and up, so no limiting.
795            loader,
796        );
797        // Handle the result
798        match res {
799            Ok(v1::InitResult::Success {
800                logs,
801                return_value: _, /* Ignore return value for now, since our tools do not support
802                                  * it for inits, currently. */
803                remaining_energy: remaining_interpreter_energy,
804                mut state,
805                trace,
806            }) => {
807                let contract_address = self.create_contract_address();
808                let mut collector = v1::trie::SizeCollector::default();
809
810                let persisted_state = state.freeze(&mut loader, &mut collector);
811
812                // Perform the subtraction in the more finegrained (*1000) `InterpreterEnergy`,
813                // and *then* convert to `Energy`. This is how it is done in the node, and if we
814                // swap the operations, it can result in a small discrepancy due to rounding.
815                let energy_used_in_interpreter = from_interpreter_energy(
816                    &energy_given_to_interpreter.saturating_sub(&remaining_interpreter_energy),
817                );
818                exit_ooe!(remaining_energy.tick_energy(energy_used_in_interpreter), trace);
819
820                // Charge one energy per stored state byte.
821                let energy_for_state_storage = Energy::from(collector.collect());
822                exit_ooe!(remaining_energy.tick_energy(energy_for_state_storage), trace);
823
824                // Charge the constant cost for initializing a contract.
825                exit_ooe!(
826                    remaining_energy
827                        .tick_energy(constants::INITIALIZE_CONTRACT_INSTANCE_CREATE_COST),
828                    trace
829                );
830
831                let contract = Contract {
832                    module_reference: payload.mod_ref,
833                    contract_name:    payload.init_name,
834                    state:            persisted_state,
835                    owner:            sender,
836                    self_balance:     payload.amount,
837                    address:          contract_address,
838                };
839
840                // Save the contract.
841                self.contracts.insert(contract_address, contract);
842
843                // Subtract the amount from the invoker.
844                self.account_mut(sender).expect("Account known to exist").balance.total -=
845                    payload.amount;
846
847                let energy_used = energy_reserved - *remaining_energy;
848                let transaction_fee = self.parameters.calculate_energy_cost(energy_used);
849
850                Ok(ContractInitSuccess {
851                    contract_address,
852                    events: contract_events_from_logs(logs),
853                    energy_used,
854                    transaction_fee,
855                    debug_trace: trace,
856                })
857            }
858            Ok(v1::InitResult::Reject {
859                reason,
860                return_value,
861                remaining_energy: remaining_interpreter_energy,
862                trace,
863            }) => {
864                let energy_used_in_interpreter = from_interpreter_energy(
865                    &energy_given_to_interpreter.saturating_sub(&remaining_interpreter_energy),
866                );
867                exit_ooe!(remaining_energy.tick_energy(energy_used_in_interpreter), trace);
868                Err(ContractInitErrorKind::ExecutionError {
869                    error:       InitExecutionError::Reject {
870                        reason,
871                        return_value,
872                    },
873                    debug_trace: trace,
874                })
875            }
876            Ok(v1::InitResult::Trap {
877                error,
878                remaining_energy: remaining_interpreter_energy,
879                trace,
880            }) => {
881                let energy_used_in_interpreter = from_interpreter_energy(
882                    &energy_given_to_interpreter.saturating_sub(&remaining_interpreter_energy),
883                );
884                exit_ooe!(remaining_energy.tick_energy(energy_used_in_interpreter), trace);
885                Err(ContractInitErrorKind::ExecutionError {
886                    error:       InitExecutionError::Trap {
887                        error: error.into(),
888                    },
889                    debug_trace: trace,
890                })
891            }
892            Ok(v1::InitResult::OutOfEnergy {
893                trace,
894            }) => {
895                *remaining_energy = Energy::from(0);
896                Err(ContractInitErrorKind::ExecutionError {
897                    error:       InitExecutionError::OutOfEnergy,
898                    debug_trace: trace,
899                })
900            }
901            Err(InvalidReturnCodeError {
902                value,
903                debug_trace,
904            }) => Err(ContractInitErrorKind::ExecutionError {
905                error: InitExecutionError::Trap {
906                    error: anyhow::anyhow!("Invalid return value received: {value:?}").into(),
907                },
908                debug_trace,
909            }),
910        }
911    }
912
913    /// Helper method that handles contract invocation.
914    ///
915    /// *Preconditions:*
916    ///  - `invoker` exists.
917    ///  - `sender` exists.
918    ///  - `invoker` has sufficient balance to pay for `energy_reserved`.
919    fn contract_invocation_worker(
920        &self,
921        invoker: AccountAddress,
922        sender: Address,
923        energy_reserved: Energy,
924        amount_reserved_for_energy: Amount,
925        payload: UpdateContractPayload,
926        remaining_energy: &mut Energy,
927    ) -> Result<(InvokeResponse, ChangeSet, Vec<DebugTraceElement>, Energy), ContractInvokeError>
928    {
929        // Check if the contract to invoke exists.
930        if !self.contract_exists(payload.address) {
931            return Err(self.convert_to_invoke_error(
932                ContractDoesNotExist {
933                    address: payload.address,
934                }
935                .into(),
936                Vec::new(),
937                energy_reserved,
938                *remaining_energy,
939                0.into(),
940            ));
941        }
942
943        // Ensure that the parameter has a valid size.
944        if payload.message.as_ref().len() > contracts_common::constants::MAX_PARAMETER_LEN {
945            return Err(self.convert_to_invoke_error(
946                ContractInvokeErrorKind::ParameterTooLarge,
947                Vec::new(),
948                energy_reserved,
949                *remaining_energy,
950                0.into(),
951            ));
952        }
953
954        // Check that the invoker has sufficient funds to pay for amount (in addition to
955        // the energy reserved, which is already checked).
956        if self
957            .account(invoker)
958            .expect("Precondition violation: must already exist")
959            .balance
960            .available()
961            < amount_reserved_for_energy + payload.amount
962        {
963            return Err(self.convert_to_invoke_error(
964                ContractInvokeErrorKind::AmountTooLarge,
965                Vec::new(),
966                energy_reserved,
967                *remaining_energy,
968                0.into(),
969            ));
970        }
971
972        let mut contract_invocation = EntrypointInvocationHandler {
973            changeset: ChangeSet::new(),
974            remaining_energy,
975            energy_reserved,
976            chain: self,
977            reserved_amount: amount_reserved_for_energy,
978            invoker,
979            // Starts at 1 since 0 is the "initial state" of all contracts in the current
980            // transaction.
981            next_contract_modification_index: 1,
982            module_load_energy: 0.into(),
983        };
984        let module_load_energy = contract_invocation.module_load_energy;
985        let res = contract_invocation.invoke_entrypoint(invoker, sender, payload);
986        match res {
987            Ok((result, trace_elements)) => Ok((
988                result,
989                contract_invocation.changeset,
990                trace_elements,
991                contract_invocation.module_load_energy,
992            )),
993            Err(err) => Err(self.convert_to_invoke_error(
994                err.into(),
995                Vec::new(),
996                energy_reserved,
997                *remaining_energy,
998                module_load_energy,
999            )),
1000        }
1001    }
1002
1003    // Since this is an internal function it seems better to allow rather than
1004    // introduce a new struct just to call this function.
1005    #[allow(clippy::too_many_arguments)]
1006    fn contract_invocation_process_response(
1007        &self,
1008        result: InvokeResponse,
1009        trace_elements: Vec<DebugTraceElement>,
1010        energy_reserved: Energy,
1011        remaining_energy: Energy,
1012        state_energy: Energy,
1013        state_changed: bool,
1014        module_load_energy: Energy,
1015    ) -> Result<ContractInvokeSuccess, ContractInvokeError> {
1016        match result {
1017            v1::InvokeResponse::Success {
1018                new_balance,
1019                data,
1020            } => {
1021                let energy_used = energy_reserved - remaining_energy;
1022                let transaction_fee = self.parameters.calculate_energy_cost(energy_used);
1023                Ok(ContractInvokeSuccess {
1024                    trace_elements,
1025                    energy_used,
1026                    transaction_fee,
1027                    return_value: data.unwrap_or_default(),
1028                    state_changed,
1029                    new_balance,
1030                    storage_energy: state_energy,
1031                    module_load_energy,
1032                })
1033            }
1034            v1::InvokeResponse::Failure {
1035                kind,
1036            } => Err(self.convert_to_invoke_error(
1037                ContractInvokeErrorKind::ExecutionError {
1038                    failure_kind: kind,
1039                },
1040                trace_elements,
1041                energy_reserved,
1042                remaining_energy,
1043                module_load_energy,
1044            )),
1045        }
1046    }
1047
1048    /// Update a contract by calling one of its entrypoints.
1049    ///
1050    /// If successful, all changes will be saved.
1051    ///
1052    /// **Parameters:**
1053    ///  - `signer`: a [`Signer`] with a number of keys. The number of keys
1054    ///    affects the cost of the transaction.
1055    ///  - `invoker`: the account paying for the transaction.
1056    ///  - `sender`: the sender of the message, can be an account or contract.
1057    ///    For top-level invocations, such as those caused by sending a contract
1058    ///    update transaction on the chain, the `sender` is always the
1059    ///    `invoker`. Here we provide extra freedom for testing invocations
1060    ///    where the sender differs.
1061    ///  - `energy_reserved`: the maximum energy that can be used in the update.
1062    ///  - `payload`: The data detailing which contract and receive method to
1063    ///    call etc.
1064    pub fn contract_update(
1065        &mut self,
1066        signer: Signer,
1067        invoker: AccountAddress,
1068        sender: Address,
1069        energy_reserved: Energy,
1070        payload: UpdateContractPayload,
1071    ) -> Result<ContractInvokeSuccess, ContractInvokeError> {
1072        // Ensure the sender exists.
1073        if !self.address_exists(sender) {
1074            // This situation never happens on the chain since to send a message the sender
1075            // is verified upfront. So what we do here is custom behaviour, and we reject
1076            // without consuming any energy.
1077            return Err(ContractInvokeError {
1078                energy_used:        Energy::from(0),
1079                transaction_fee:    Amount::zero(),
1080                trace_elements:     Vec::new(),
1081                kind:               ContractInvokeErrorKind::SenderDoesNotExist(sender),
1082                module_load_energy: 0.into(),
1083            });
1084        }
1085
1086        // Ensure the invoker exists.
1087        let Ok(account_info) = self.account(invoker) else {
1088            return Err(ContractInvokeError {
1089                energy_used:        Energy::from(0),
1090                transaction_fee:    Amount::zero(),
1091                trace_elements:     Vec::new(),
1092                kind:               ContractInvokeErrorKind::InvokerDoesNotExist(
1093                    AccountDoesNotExist {
1094                        address: invoker,
1095                    },
1096                ),
1097                module_load_energy: 0.into(),
1098            });
1099        };
1100
1101        // Compute the base cost for checking the transaction header.
1102        let check_header_cost = {
1103            // 1 byte for the tag.
1104            let transaction_size =
1105                transactions::construct::TRANSACTION_HEADER_SIZE + 1 + payload.size() as u64;
1106            transactions::cost::base_cost(transaction_size, signer.num_keys)
1107        };
1108
1109        // Charge the header cost.
1110        let mut remaining_energy =
1111            energy_reserved.checked_sub(check_header_cost).ok_or(ContractInvokeError {
1112                energy_used:        Energy::from(0),
1113                transaction_fee:    Amount::zero(),
1114                trace_elements:     Vec::new(),
1115                kind:               ContractInvokeErrorKind::OutOfEnergy {
1116                    debug_trace: DebugTracker::empty_trace(), // we haven't done anything yet.
1117                },
1118                module_load_energy: 0.into(),
1119            })?;
1120
1121        let invoker_amount_reserved_for_nrg =
1122            self.parameters.calculate_energy_cost(energy_reserved);
1123
1124        // Ensure the account has sufficient funds to pay for the energy.
1125        if account_info.balance.available() < invoker_amount_reserved_for_nrg {
1126            let energy_used = energy_reserved - remaining_energy;
1127            return Err(ContractInvokeError {
1128                energy_used,
1129                transaction_fee: self.parameters.calculate_energy_cost(energy_used),
1130                trace_elements: Vec::new(),
1131                kind: ContractInvokeErrorKind::InsufficientFunds,
1132                module_load_energy: 0.into(),
1133            });
1134        }
1135
1136        let contract_address = payload.address;
1137        let res = self.contract_invocation_worker(
1138            invoker,
1139            sender,
1140            energy_reserved,
1141            invoker_amount_reserved_for_nrg,
1142            payload,
1143            &mut remaining_energy,
1144        );
1145        let res = match res {
1146            Ok((result, changeset, trace_elements, module_load_energy)) => {
1147                // Charge energy for contract storage. Or return an error if out
1148                // of energy.
1149                let (state_energy, state_changed) =
1150                    if matches!(result, v1::InvokeResponse::Success { .. }) {
1151                        let energy_before = remaining_energy;
1152                        let res = changeset.persist(
1153                            &mut remaining_energy,
1154                            contract_address,
1155                            &mut self.accounts,
1156                            &mut self.contracts,
1157                        );
1158                        let state_energy = energy_before.checked_sub(remaining_energy).unwrap();
1159                        if let Ok(res) = res {
1160                            (state_energy, res)
1161                        } else {
1162                            // the error happens when storing the state, so there are no trace
1163                            // elements associated with it. The trace is
1164                            // already in the "debug trace" vector.
1165                            return Err(self.invocation_out_of_energy_error(
1166                                energy_reserved,
1167                                DebugTracker::empty_trace(),
1168                                module_load_energy,
1169                            ));
1170                        }
1171                    } else {
1172                        // An error occurred, so state hasn't changed.
1173                        (0.into(), false)
1174                    };
1175                self.contract_invocation_process_response(
1176                    result,
1177                    trace_elements,
1178                    energy_reserved,
1179                    remaining_energy,
1180                    state_energy,
1181                    state_changed,
1182                    module_load_energy,
1183                )
1184            }
1185            Err(e) => Err(e),
1186        };
1187
1188        let transaction_fee = match &res {
1189            Ok(s) => s.transaction_fee,
1190            Err(e) => e.transaction_fee,
1191        };
1192        // Charge for execution.
1193        self.account_mut(invoker).expect("existence already checked").balance.total -=
1194            transaction_fee;
1195        res
1196    }
1197
1198    /// Invoke a contract by calling an entrypoint.
1199    ///
1200    /// Similar to [`Chain::contract_update`](Self::contract_update) except that
1201    /// all changes are discarded afterwards. Typically used for "view"
1202    /// functions.
1203    ///
1204    /// **Parameters:**
1205    ///  - `invoker`: the account used as invoker. Since this isn't a
1206    ///    transaction, it won't be charged.
1207    ///  - `sender`: the sender. Can be either a contract address or an account
1208    ///    address.
1209    ///  - `energy_reserved`: the maximum energy that can be used in the update.
1210    ///  - `payload`: The data detailing which contract and receive method to
1211    ///    call etc.
1212    pub fn contract_invoke(
1213        &self,
1214        invoker: AccountAddress,
1215        sender: Address,
1216        energy_reserved: Energy,
1217        payload: UpdateContractPayload,
1218    ) -> Result<ContractInvokeSuccess, ContractInvokeError> {
1219        // Ensure the sender exists.
1220        if !self.address_exists(sender) {
1221            return Err(ContractInvokeError {
1222                energy_used:        Energy::from(0),
1223                transaction_fee:    Amount::zero(),
1224                trace_elements:     Vec::new(),
1225                kind:               ContractInvokeErrorKind::SenderDoesNotExist(sender),
1226                module_load_energy: 0.into(),
1227            });
1228        }
1229
1230        let Some(account_info) = self.accounts.get(&invoker.into()) else {
1231            return Err(ContractInvokeError {
1232                energy_used:        Energy::from(0),
1233                transaction_fee:    Amount::zero(),
1234                trace_elements:     Vec::new(),
1235                kind:               ContractInvokeErrorKind::InvokerDoesNotExist(
1236                    AccountDoesNotExist {
1237                        address: invoker,
1238                    },
1239                ),
1240                module_load_energy: 0.into(),
1241            });
1242        };
1243
1244        let invoker_amount_reserved_for_nrg =
1245            self.parameters.calculate_energy_cost(energy_reserved);
1246
1247        if account_info.balance.available() < invoker_amount_reserved_for_nrg {
1248            let energy_used = Energy::from(0);
1249            return Err(ContractInvokeError {
1250                energy_used,
1251                transaction_fee: self.parameters.calculate_energy_cost(energy_used),
1252                trace_elements: Vec::new(),
1253                kind: ContractInvokeErrorKind::InsufficientFunds,
1254                module_load_energy: 0.into(),
1255            });
1256        }
1257
1258        let mut remaining_energy = energy_reserved;
1259
1260        let contract_address = payload.address;
1261
1262        let res = self.contract_invocation_worker(
1263            invoker,
1264            sender,
1265            energy_reserved,
1266            invoker_amount_reserved_for_nrg,
1267            payload,
1268            &mut remaining_energy,
1269        );
1270        match res {
1271            Ok((result, changeset, trace_elements, module_load_energy)) => {
1272                // Charge energy for contract storage. Or return an error if out
1273                // of energy.
1274                let (state_energy, state_changed) =
1275                    if matches!(result, v1::InvokeResponse::Success { .. }) {
1276                        let energy_before = remaining_energy;
1277                        if let Ok(state_changed) = changeset
1278                            .collect_energy_for_state(&mut remaining_energy, contract_address)
1279                        {
1280                            let state_energy = energy_before.checked_sub(remaining_energy).unwrap();
1281                            (state_energy, state_changed)
1282                        } else {
1283                            // the error happens when storing the state, so there are no trace
1284                            // elements associated with it. The trace is
1285                            // already in the "debug trace" vector.
1286                            return Err(self.invocation_out_of_energy_error(
1287                                energy_reserved,
1288                                DebugTracker::empty_trace(),
1289                                module_load_energy,
1290                            ));
1291                        }
1292                    } else {
1293                        // An error occurred, so state hasn't changed.
1294                        (0.into(), false)
1295                    };
1296                self.contract_invocation_process_response(
1297                    result,
1298                    trace_elements,
1299                    energy_reserved,
1300                    remaining_energy,
1301                    state_energy,
1302                    state_changed,
1303                    module_load_energy,
1304                )
1305            }
1306            Err(e) => Err(e),
1307        }
1308    }
1309
1310    /// Invoke an external contract entrypoint.
1311    ///
1312    /// Similar to [`Chain::contract_invoke`](Self::contract_invoke) except that
1313    /// it invokes/dry runs a contract on the external node.
1314    ///
1315    /// **Parameters:**
1316    ///  - `invoker`: the account used as invoker.
1317    ///     - The account must exist on the connected node.
1318    ///  - `sender`: the sender, can also be a contract.
1319    ///     - The sender must exist on the connected node.
1320    ///  - `energy_reserved`: the maximum energy that can be used in the update.
1321    ///  - `payload`: The data detailing which contract and receive method to
1322    ///    call etc.
1323    ///  - `block`: The block in which the invocation will be simulated, as if
1324    ///    it was at the end of the block. If `None` is provided, the
1325    ///    `external_query_block` is used instead.
1326    ///
1327    ///  # Example:
1328    ///
1329    ///  ```no_run
1330    ///  # use concordium_smart_contract_testing::*;
1331    ///  let mut chain = Chain::builder()
1332    ///                     .external_node_connection(Endpoint::from_static("http://node.testnet.concordium.com:20000"))
1333    ///                     .build()
1334    ///                     .unwrap();
1335    ///
1336    ///  // Set up an external contract.
1337    ///  let external_contract =
1338    /// chain.add_external_contract(ContractAddress::new(1010, 0)).unwrap();
1339    ///
1340    ///  // Set up an external account.
1341    ///  let external_acc =
1342    /// chain.add_external_account("
1343    /// 3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini".parse().unwrap()).
1344    /// unwrap();
1345    ///
1346    /// let res = chain.contract_invoke_external(
1347    ///     Some(ExternalAddress::Account(external_acc)),
1348    ///     10000.into(),
1349    ///     InvokeExternalContractPayload {
1350    ///         amount:       Amount::zero(),
1351    ///         address:      external_contract,
1352    ///         receive_name:
1353    /// OwnedReceiveName::new_unchecked("my_contract.view".to_string()),
1354    ///         message:      OwnedParameter::empty(),
1355    ///     },
1356    ///     None,
1357    /// );
1358    /// ```
1359    pub fn contract_invoke_external(
1360        &self,
1361        sender: Option<ExternalAddress>,
1362        energy_reserved: Energy,
1363        payload: InvokeExternalContractPayload,
1364        block: Option<BlockHash>,
1365    ) -> Result<ContractInvokeExternalSuccess, ContractInvokeExternalError> {
1366        let connection = self.external_node_connection().unwrap();
1367
1368        // Make the invocation.
1369        let invoke_result: InvokeContractResult =
1370            connection.with_client(block, |block_identifier, mut client| async move {
1371                let invoke_result = client
1372                    .invoke_instance(
1373                        block_identifier,
1374                        &sdk::types::smart_contracts::ContractContext {
1375                            invoker:   sender.map(|ext_addr| ext_addr.to_address()),
1376                            contract:  payload.address.address,
1377                            amount:    payload.amount,
1378                            method:    payload.receive_name,
1379                            parameter: payload.message,
1380                            energy:    Some(energy_reserved),
1381                        },
1382                    )
1383                    .await?
1384                    .response;
1385                Ok::<_, ExternalNodeError>(invoke_result)
1386            })?;
1387
1388        // Convert the result.
1389        match invoke_result {
1390            InvokeContractResult::Success {
1391                return_value,
1392                events,
1393                used_energy,
1394            } => Ok(ContractInvokeExternalSuccess {
1395                trace_elements: events,
1396                energy_used:    used_energy,
1397                return_value:   return_value.map(|rv| rv.value).unwrap_or_default(),
1398            }),
1399            InvokeContractResult::Failure {
1400                return_value,
1401                reason,
1402                used_energy,
1403            } => Err(ContractInvokeExternalError::Failure {
1404                reason,
1405                energy_used: used_energy,
1406                return_value: return_value.map(|rv| rv.value).unwrap_or_default(),
1407            }),
1408        }
1409    }
1410
1411    /// Create an account.
1412    ///
1413    /// If an account with a matching address already exists this method will
1414    /// replace it and return the old account.
1415    ///
1416    /// Note that if the first 29-bytes of an account are identical, then
1417    /// they are *considered aliases* on each other in all methods.
1418    /// See the example below:
1419    ///
1420    /// ```
1421    /// # use concordium_smart_contract_testing::*;
1422    /// let mut chain = Chain::new();
1423    /// let acc = AccountAddress([
1424    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1425    ///     0, 0,
1426    /// ]);
1427    /// let acc_alias = AccountAddress([
1428    ///     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1429    ///     2, 3, // Only last three bytes differ.
1430    /// ]);
1431    ///
1432    /// chain.create_account(Account::new(acc, Amount::from_ccd(123)));
1433    /// assert_eq!(
1434    ///     chain.account_balance_available(acc_alias), // Using the alias for lookup.
1435    ///     Some(Amount::from_ccd(123))
1436    /// );
1437    /// ```
1438    pub fn create_account(&mut self, account: Account) -> Option<Account> {
1439        self.accounts.insert(account.address.into(), account)
1440    }
1441
1442    /// Add an external account from a connected external node.
1443    ///
1444    /// If the account exists on the external node at the time of the
1445    /// `external_query_block`, then an [`ExternalAccountAddress`] is returned.
1446    /// The address can be used with [`Chain::contract_invoke_external`].
1447    /// Otherwise, an error is returned.
1448    ///
1449    /// Barring external node errors, the method is idempotent, and so it can be
1450    /// called multiple times with the same effect.
1451    pub fn add_external_account(
1452        &mut self,
1453        address: AccountAddress,
1454    ) -> Result<ExternalAccountAddress, ExternalNodeError> {
1455        let connection = self.external_node_connection_mut()?;
1456
1457        let external_addr =
1458            connection.with_client(None, |block_identifier, mut client| async move {
1459                // Try to get the account info to verify the existence of the account, but
1460                // discard the result.
1461                client
1462                    .get_account_info(
1463                        &sdk::v2::AccountIdentifier::Address(address),
1464                        block_identifier,
1465                    )
1466                    .await?;
1467                Ok::<_, ExternalNodeError>(ExternalAccountAddress {
1468                    address,
1469                })
1470            })?;
1471
1472        connection.accounts.insert(external_addr);
1473
1474        Ok(external_addr)
1475    }
1476
1477    /// Add an external contract from a connected external node.
1478    ///
1479    /// If the contract exists on the external node at the time of the
1480    /// `external_query_block`, then an [`ExternalContractAddress`] is returned.
1481    /// The address can be used with [`Chain::contract_invoke_external`].
1482    /// Otherwise, an error is returned.
1483    ///
1484    /// Barring external node errors, the method is idempotent, and so it can be
1485    /// called multiple times with the same effect.
1486    pub fn add_external_contract(
1487        &mut self,
1488        address: ContractAddress,
1489    ) -> Result<ExternalContractAddress, ExternalNodeError> {
1490        let connection = self.external_node_connection_mut()?;
1491
1492        let external_addr =
1493            connection.with_client(None, |block_identifier, mut client| async move {
1494                // Try to get the contract instance info to verify the existence of the
1495                // contract, but discard the result.
1496                client.get_instance_info(address, block_identifier).await?;
1497                Ok::<_, ExternalNodeError>(ExternalContractAddress {
1498                    address,
1499                })
1500            })?;
1501
1502        connection.contracts.insert(external_addr);
1503
1504        Ok(external_addr)
1505    }
1506
1507    /// Create a contract address by giving it the next available index.
1508    fn create_contract_address(&mut self) -> ContractAddress {
1509        let index = self.next_contract_index;
1510        let subindex = 0;
1511        self.next_contract_index += 1;
1512        ContractAddress::new(index, subindex)
1513    }
1514
1515    /// Returns the balance of an account if it exists.
1516    pub fn account_balance(&self, address: AccountAddress) -> Option<AccountBalance> {
1517        self.accounts.get(&address.into()).map(|ai| ai.balance)
1518    }
1519
1520    /// Returns the available balance of an account if it exists.
1521    pub fn account_balance_available(&self, address: AccountAddress) -> Option<Amount> {
1522        self.accounts.get(&address.into()).map(|ai| ai.balance.available())
1523    }
1524
1525    /// Returns the balance of an contract if it exists.
1526    pub fn contract_balance(&self, address: ContractAddress) -> Option<Amount> {
1527        self.contracts.get(&address).map(|ci| ci.self_balance)
1528    }
1529
1530    /// Helper method for looking up part of the state of a smart contract,
1531    /// which is a key-value store.
1532    pub fn contract_state_lookup(&self, address: ContractAddress, key: &[u8]) -> Option<Vec<u8>> {
1533        let mut loader = v1::trie::Loader::new(&[][..]);
1534        self.contracts.get(&address)?.state.lookup(&mut loader, key)
1535    }
1536
1537    /// Return a clone of the [`ContractModule`] (which has an `Arc` around the
1538    /// artifact so cloning is cheap).
1539    fn contract_module(
1540        &self,
1541        module_ref: ModuleReference,
1542    ) -> Result<ContractModule, ModuleDoesNotExist> {
1543        let module = self.modules.get(&module_ref).ok_or(ModuleDoesNotExist {
1544            module_reference: module_ref,
1545        })?;
1546        Ok(module.clone())
1547    }
1548
1549    /// Returns an immutable reference to an [`Account`].
1550    pub fn account(&self, address: AccountAddress) -> Result<&Account, AccountDoesNotExist> {
1551        self.accounts.get(&address.into()).ok_or(AccountDoesNotExist {
1552            address,
1553        })
1554    }
1555
1556    /// Returns a mutable reference to [`Account`].
1557    fn account_mut(
1558        &mut self,
1559        address: AccountAddress,
1560    ) -> Result<&mut Account, AccountDoesNotExist> {
1561        self.accounts.get_mut(&address.into()).ok_or(AccountDoesNotExist {
1562            address,
1563        })
1564    }
1565
1566    /// Check whether an [`Account`] exists.
1567    pub fn account_exists(&self, address: AccountAddress) -> bool {
1568        self.accounts.contains_key(&address.into())
1569    }
1570
1571    /// Check whether a [`Contract`] exists.
1572    pub fn contract_exists(&self, address: ContractAddress) -> bool {
1573        self.contracts.contains_key(&address)
1574    }
1575
1576    /// Check whether an object with the [`Address`] exists.
1577    ///
1578    /// That is, if it is an account address, whether the account exists,
1579    /// and if it is a contract address, whether the contract exists.
1580    fn address_exists(&self, address: Address) -> bool {
1581        match address {
1582            Address::Account(acc) => self.account_exists(acc),
1583            Address::Contract(contr) => self.contract_exists(contr),
1584        }
1585    }
1586
1587    /// Convert a [`ContractInvokeErrorKind`] to a
1588    /// [`ContractInvokeError`] by calculating the `energy_used` and
1589    /// `transaction_fee`.
1590    ///
1591    /// If the `kind` is an out of energy, then `0` is used instead of the
1592    /// `remaining_energy` parameter, as it will likely not be `0` due to short
1593    /// circuiting during execution.
1594    fn convert_to_invoke_error(
1595        &self,
1596        kind: ContractInvokeErrorKind,
1597        trace_elements: Vec<DebugTraceElement>,
1598        energy_reserved: Energy,
1599        remaining_energy: Energy,
1600        module_load_energy: Energy,
1601    ) -> ContractInvokeError {
1602        let remaining_energy = if matches!(kind, ContractInvokeErrorKind::OutOfEnergy { .. }) {
1603            0.into()
1604        } else {
1605            remaining_energy
1606        };
1607        let energy_used = energy_reserved - remaining_energy;
1608        let transaction_fee = self.parameters.calculate_energy_cost(energy_used);
1609        ContractInvokeError {
1610            energy_used,
1611            transaction_fee,
1612            trace_elements,
1613            kind,
1614            module_load_energy,
1615        }
1616    }
1617
1618    /// Construct a [`ContractInvokeErrorKind`] of the `OutOfEnergy` kind with
1619    /// the energy and transaction fee fields based on the `energy_reserved`
1620    /// parameter.
1621    fn invocation_out_of_energy_error(
1622        &self,
1623        energy_reserved: Energy,
1624        debug_trace: DebugTracker,
1625        module_load_energy: Energy,
1626    ) -> ContractInvokeError {
1627        self.convert_to_invoke_error(
1628            ContractInvokeErrorKind::OutOfEnergy {
1629                debug_trace,
1630            },
1631            Vec::new(),
1632            energy_reserved,
1633            Energy::from(0),
1634            module_load_energy,
1635        )
1636    }
1637
1638    /// Convert a [`ContractInitErrorKind`] to a
1639    /// [`ContractInitError`] by calculating the `energy_used` and
1640    /// `transaction_fee`.
1641    fn convert_to_init_error(
1642        &self,
1643        kind: ContractInitErrorKind,
1644        energy_reserved: Energy,
1645        remaining_energy: Energy,
1646    ) -> ContractInitError {
1647        let energy_used = energy_reserved - remaining_energy;
1648        let transaction_fee = self.parameters.calculate_energy_cost(energy_used);
1649        ContractInitError {
1650            energy_used,
1651            transaction_fee,
1652            kind,
1653        }
1654    }
1655
1656    /// Try to set the exchange rates on the chain.
1657    ///
1658    /// Will fail if they result in the cost of one energy being larger than
1659    /// `u64::MAX / 100_000_000_000`.
1660    pub fn set_exchange_rates(
1661        &mut self,
1662        micro_ccd_per_euro: ExchangeRate,
1663        euro_per_energy: ExchangeRate,
1664    ) -> Result<(), ExchangeRateError> {
1665        // Ensure the exchange rates are within a valid range.
1666        check_exchange_rates(euro_per_energy, micro_ccd_per_euro)?;
1667        self.parameters.micro_ccd_per_euro = micro_ccd_per_euro;
1668        self.parameters.euro_per_energy = euro_per_energy;
1669        Ok(())
1670    }
1671
1672    /// Get the microCCD per euro and euro per energy exchange rates by querying
1673    /// an external node using the external query block.
1674    fn get_exchange_rates_via_external_node(&self) -> Result<ExchangeRates, ExternalNodeError> {
1675        let connection = self.external_node_connection()?;
1676
1677        // Get the values from the external node.
1678        connection.with_client(None, |block_identifier, mut client| async move {
1679            let (euro_per_energy, micro_ccd_per_euro) =
1680                match client.get_block_chain_parameters(block_identifier).await?.response {
1681                    sdk::v2::ChainParameters::V0(p) => (p.euro_per_energy, p.micro_ccd_per_euro),
1682                    sdk::v2::ChainParameters::V1(p) => (p.euro_per_energy, p.micro_ccd_per_euro),
1683                    sdk::v2::ChainParameters::V2(p) => (p.euro_per_energy, p.micro_ccd_per_euro),
1684                    sdk::v2::ChainParameters::V3(p) => (p.euro_per_energy, p.micro_ccd_per_euro),
1685                };
1686            Ok(ExchangeRates {
1687                euro_per_energy,
1688                micro_ccd_per_euro,
1689            })
1690        })
1691    }
1692
1693    /// Tick the block time on the [`Chain`] by a [`Duration`].
1694    ///
1695    /// Returns an error if ticking causes the block time to overflow.
1696    ///
1697    /// # Example
1698    ///
1699    /// ```
1700    /// # use concordium_smart_contract_testing::*;
1701    ///
1702    /// // Block time defaults to 0.
1703    /// let mut chain = Chain::new();
1704    ///
1705    /// // Increase block time by 123 milliseconds.
1706    /// chain.tick_block_time(Duration::from_millis(123)).unwrap();
1707    ///
1708    /// // Block time has now increased.
1709    /// assert_eq!(chain.block_time(), Timestamp::from_timestamp_millis(123));
1710    /// ```
1711    pub fn tick_block_time(&mut self, duration: Duration) -> Result<(), BlockTimeOverflow> {
1712        self.parameters.block_time =
1713            self.parameters.block_time.checked_add(duration).ok_or(BlockTimeOverflow)?;
1714        Ok(())
1715    }
1716
1717    /// Set the block time by querying the external node.
1718    ///
1719    /// The default query block is always used.
1720    ///
1721    /// The external node must be setup prior to this call via the method
1722    /// [`Chain::setup_external_node_connection`], otherwise an error is
1723    /// returned.
1724    fn set_block_time_via_external_node(&mut self) -> Result<(), ExternalNodeError> {
1725        let connection = self.external_node_connection_mut()?;
1726
1727        // Get the timestamp in milliseconds.
1728        let timestamp =
1729            connection.with_client(None, |block_identifier, mut client| async move {
1730                Ok(client
1731                    .get_block_info(block_identifier)
1732                    .await?
1733                    .response
1734                    .block_slot_time
1735                    .timestamp_millis() as u64) // The node never returns
1736                                                // timestamps < 0, so it is safe
1737                                                // to cast it to `u64`.
1738            })?;
1739        // Update the block time.
1740        self.parameters.block_time = Timestamp::from_timestamp_millis(timestamp);
1741
1742        Ok(())
1743    }
1744
1745    /// Set up a connection to an external Concordium node.
1746    ///
1747    /// This method also queries the block info for one of two reasons:
1748    /// 1) If `query_block` is provided, its existence is checked.
1749    /// 2) Otherwise, the last final block is queried to get its blockhash which
1750    ///    will be saved in [`ExternalNodeConnection`].
1751    fn setup_external_node_connection(
1752        &mut self,
1753        endpoint: Endpoint,
1754        query_block: Option<BlockHash>,
1755    ) -> Result<(), SetupExternalNodeError> {
1756        // Create the Tokio runtime. This should never fail, unless nested runtimes are
1757        // created.
1758        let runtime = runtime::Builder::new_multi_thread()
1759            // Enable time, so timeouts can be used.
1760            .enable_time()
1761            // Enable I/O, so networking and other types of calls are possible.
1762            .enable_io()
1763            .build()
1764            .expect("Internal error: Could not create Tokio runtime.");
1765
1766        // A future for getting the client. Executed below.
1767        let get_client = async {
1768            // Try to create the client, which also checks that the connection is valid.
1769            let client = sdk::v2::Client::new(endpoint).await?;
1770            Ok::<sdk::v2::Client, SetupExternalNodeError>(client)
1771        };
1772
1773        // A future for checking the block.
1774        let get_block_info = |mut client: sdk::v2::Client| async move {
1775            let block_identifier = if let Some(query_block) = query_block {
1776                sdk::v2::BlockIdentifier::Given(query_block)
1777            } else {
1778                sdk::v2::BlockIdentifier::LastFinal
1779            };
1780
1781            let block_hash = match client.get_block_info(block_identifier).await {
1782                Ok(res) => res.block_hash,
1783                Err(sdk::v2::QueryError::NotFound) => {
1784                    return Err(SetupExternalNodeError::QueryBlockDoesNotExist {
1785                        // It should never be possible to get `NotFound` when querying `LastFinal`,
1786                        // and so, the `query_block` must be `Some`.
1787                        query_block: query_block.expect(
1788                            "Internal error: Got `QueryError::NotFound` for when querying last \
1789                             final block.",
1790                        ),
1791                    });
1792                }
1793                Err(sdk::v2::QueryError::RPCError(error)) => {
1794                    return Err(SetupExternalNodeError::CannotCheckQueryBlockExistence {
1795                        error,
1796                    })
1797                }
1798            };
1799            Ok(block_hash)
1800        };
1801
1802        // Get the client synchronously by blocking until the async returns.
1803        let (client, checked_query_block) = runtime.block_on(async {
1804            let client = timeout(EXTERNAL_NODE_CONNECT_TIMEOUT, get_client)
1805                .await
1806                .map_err(|_| SetupExternalNodeError::ConnectTimeout)??;
1807            let checked_query_block =
1808                timeout(EXTERNAL_NODE_QUERY_TIMEOUT, get_block_info(client.clone()))
1809                    .await
1810                    .map_err(|_| SetupExternalNodeError::CheckQueryBlockTimeout)??;
1811            Ok::<_, SetupExternalNodeError>((client, checked_query_block))
1812        })?;
1813
1814        // Set or replace the node connection.
1815        self.external_node_connection = Some(ExternalNodeConnection {
1816            client,
1817            runtime,
1818            query_block: checked_query_block,
1819            accounts: BTreeSet::new(),
1820            contracts: BTreeSet::new(),
1821        });
1822
1823        Ok(())
1824    }
1825
1826    /// Try to get a mutable reference to [`ExternalNodeConnection`] or return
1827    /// an error.
1828    ///
1829    /// The connection is only available, if the [`Chain`] has been set up with
1830    /// an external node connection via
1831    /// [`ChainBuilder::external_node_connection`] in the [`ChainBuilder`].
1832    fn external_node_connection_mut(
1833        &mut self,
1834    ) -> Result<&mut ExternalNodeConnection, ExternalNodeNotConfigured> {
1835        match &mut self.external_node_connection {
1836            None => Err(ExternalNodeNotConfigured),
1837            Some(data) => Ok(data),
1838        }
1839    }
1840
1841    /// Try to get an immutable reference to [`ExternalNodeConnection`] or
1842    /// return an error.
1843    ///
1844    /// The connection is only available, if the [`Chain`] has been set up with
1845    /// an external node connection via
1846    /// [`ChainBuilder::external_node_connection`] in the [`ChainBuilder`].
1847    fn external_node_connection(
1848        &self,
1849    ) -> Result<&ExternalNodeConnection, ExternalNodeNotConfigured> {
1850        match &self.external_node_connection {
1851            None => Err(ExternalNodeNotConfigured),
1852            Some(data) => Ok(data),
1853        }
1854    }
1855
1856    /// Return the current microCCD per euro exchange rate.
1857    pub fn micro_ccd_per_euro(&self) -> ExchangeRate { self.parameters.micro_ccd_per_euro }
1858
1859    /// Return the current euro per energy exchange rate.
1860    pub fn euro_per_energy(&self) -> ExchangeRate { self.parameters.euro_per_energy }
1861
1862    /// Return the current block time.
1863    pub fn block_time(&self) -> Timestamp { self.parameters.block_time }
1864
1865    /// Return the block used for external queries by default.
1866    ///
1867    /// The block can be set with [`ChainBuilder::external_query_block`] when
1868    /// building the [`Chain`].
1869    ///
1870    /// This method returns an error if the external node has not been
1871    /// configured.
1872    pub fn external_query_block(&self) -> Result<BlockHash, ExternalNodeNotConfigured> {
1873        self.external_node_connection().map(|conn| conn.query_block)
1874    }
1875}
1876
1877impl ExternalNodeConnection {
1878    /// Execute an async task with the [`sdk::v2::Client`].
1879    ///
1880    /// If a block is provided, it will be used for the query. Otherwise, it
1881    /// will use the default query block.
1882    ///
1883    /// If the task takes longer than [`EXTERNAL_NODE_TIMEOUT_DURATION`] then
1884    /// the connection times out and an [`Err(ExternalNodeError::Timeout)`] is
1885    /// returned.
1886    ///
1887    /// *This method cannot be nested, as that will cause a panic.*
1888    fn with_client<T, F, Fut>(
1889        &self,
1890        block: Option<BlockHash>,
1891        f: F,
1892    ) -> Result<T, ExternalNodeError>
1893    where
1894        F: FnOnce(sdk::v2::BlockIdentifier, sdk::v2::Client) -> Fut,
1895        Fut: Future<Output = Result<T, ExternalNodeError>>, {
1896        // Get the block identifier, either using the provided block or the default
1897        // query block.
1898        let block_identifier = if let Some(block) = block {
1899            sdk::v2::BlockIdentifier::Given(block)
1900        } else {
1901            sdk::v2::BlockIdentifier::Given(self.query_block)
1902        };
1903        // Clone the client so it can be moved to the async block.
1904        let client = self.client.clone();
1905        // Run the future and timeout if it takes too long.
1906        self.runtime.block_on(async move {
1907            timeout(EXTERNAL_NODE_QUERY_TIMEOUT, f(block_identifier, client))
1908                .await
1909                .map_err(|_| ExternalNodeError::QueryTimeout)?
1910        })
1911    }
1912}
1913
1914impl Account {
1915    /// Create new [`Account`](Self) with the provided account policy and keys.
1916    pub fn new_with_policy_and_keys(
1917        address: AccountAddress,
1918        balance: AccountBalance,
1919        policy: OwnedPolicy,
1920        keys: AccountAccessStructure,
1921    ) -> Self {
1922        Self {
1923            balance,
1924            policy,
1925            address,
1926            keys,
1927        }
1928    }
1929
1930    /// Create new [`Account`](Self) with the provided account keys and balance.
1931    /// See [`new`][Self::new] for what the default policy is.
1932    pub fn new_with_keys(
1933        address: AccountAddress,
1934        balance: AccountBalance,
1935        keys: AccountAccessStructure,
1936    ) -> Self {
1937        Self {
1938            balance,
1939            policy: Self::empty_policy(),
1940            address,
1941            keys,
1942        }
1943    }
1944
1945    /// Create new [`Account`](Self) with the provided account policy.
1946    /// The account keys are initialized with an [`AccountAccessStructure`]
1947    /// with a threshold of 1, and no keys. So it is impossible to verify any
1948    /// signatures with the access structure.
1949    pub fn new_with_policy(
1950        address: AccountAddress,
1951        balance: AccountBalance,
1952        policy: OwnedPolicy,
1953    ) -> Self {
1954        Self::new_with_policy_and_keys(address, balance, policy, AccountAccessStructure {
1955            threshold: AccountThreshold::try_from(1u8).expect("1 is a valid threshold."),
1956            keys:      BTreeMap::new(),
1957        })
1958    }
1959
1960    /// Create a new [`Account`](Self) with the provided balance and a default
1961    /// account policy and default account access structure.
1962    ///
1963    /// See [`new`][Self::new] for what the default policy is.
1964    pub fn new_with_balance(address: AccountAddress, balance: AccountBalance) -> Self {
1965        Self::new_with_policy(address, balance, Self::empty_policy())
1966    }
1967
1968    /// Create new [`Account`](Self) with the provided total balance.
1969    ///
1970    /// The `policy` will have:
1971    ///   - `identity_provider`: 0,
1972    ///   - `created_at`: unix epoch,
1973    ///   - `valid_to`: unix epoch + `u64::MAX` milliseconds,
1974    ///   - `items`: none,
1975    ///
1976    /// The account keys are initialized with an [`AccountAccessStructure`]
1977    /// with a threshold of 1, and no keys. So it is impossible to verify any
1978    /// signatures with the access structure.
1979    ///
1980    /// The [`AccountBalance`] will be created with the provided
1981    /// `total_balance`.
1982    pub fn new(address: AccountAddress, total_balance: Amount) -> Self {
1983        Self::new_with_policy(
1984            address,
1985            AccountBalance {
1986                total:  total_balance,
1987                staked: Amount::zero(),
1988                locked: Amount::zero(),
1989            },
1990            Self::empty_policy(),
1991        )
1992    }
1993
1994    /// Helper for creating an empty policy.
1995    ///
1996    /// It has identity provider `0`, no items, and is valid from unix epoch
1997    /// until unix epoch + u64::MAX milliseconds.
1998    fn empty_policy() -> OwnedPolicy {
1999        OwnedPolicy {
2000            identity_provider: 0,
2001            created_at:        Timestamp::from_timestamp_millis(0),
2002            valid_to:          Timestamp::from_timestamp_millis(u64::MAX),
2003            items:             Vec::new(),
2004        }
2005    }
2006}
2007
2008/// Load a raw wasm module, i.e. one **without** the prefix of 4 version
2009/// bytes and 4 module length bytes.
2010/// The module still has to be a valid V1 smart contract module.
2011pub fn module_load_v1_raw(module_path: impl AsRef<Path>) -> Result<WasmModule, ModuleLoadError> {
2012    let module_path = module_path.as_ref();
2013    // To avoid reading a giant file, we open the file for reading, check its size
2014    // and then load the contents.
2015    let (mut reader, metadata) = std::fs::File::open(module_path)
2016        .and_then(|reader| reader.metadata().map(|metadata| (reader, metadata)))
2017        .map_err(|e| ModuleLoadError {
2018            path: module_path.to_path_buf(),
2019            kind: e.into(),
2020        })?;
2021    if metadata.len() > MAX_WASM_MODULE_SIZE.into() {
2022        return Err(ModuleLoadError {
2023            path: module_path.to_path_buf(),
2024            kind: ModuleLoadErrorKind::ReadModule(
2025                anyhow!("Maximum size of a Wasm module is {}", MAX_WASM_MODULE_SIZE).into(),
2026            ),
2027        });
2028    }
2029    // We cannot deserialize directly to [`ModuleSource`] as it expects the first
2030    // four bytes to be the length, which it isn't for this raw file.
2031    let mut buffer = Vec::new();
2032    std::io::Read::read_to_end(&mut reader, &mut buffer).map_err(|e| ModuleLoadError {
2033        path: module_path.to_path_buf(),
2034        kind: ModuleLoadErrorKind::OpenFile(e), /* This is unlikely to happen, since
2035                                                 * we already opened it. */
2036    })?;
2037    Ok(WasmModule {
2038        version: WasmVersion::V1,
2039        source:  ModuleSource::from(buffer),
2040    })
2041}
2042
2043/// Load a v1 wasm module as it is output from `cargo concordium build`,
2044/// i.e. **including** the prefix of 4 version bytes and 4 module length
2045/// bytes.
2046pub fn module_load_v1(module_path: impl AsRef<Path>) -> Result<WasmModule, ModuleLoadError> {
2047    let module_path = module_path.as_ref();
2048    // To avoid reading a giant file, we just open the file for reading and then
2049    // parse it as a wasm module, which checks the length up front.
2050    let mut reader = std::fs::File::open(module_path).map_err(|e| ModuleLoadError {
2051        path: module_path.to_path_buf(),
2052        kind: e.into(),
2053    })?;
2054    let module: WasmModule =
2055        base::common::from_bytes(&mut reader).map_err(|e| ModuleLoadError {
2056            path: module_path.to_path_buf(),
2057            kind: ModuleLoadErrorKind::ReadModule(e.into()),
2058        })?;
2059    if module.version != WasmVersion::V1 {
2060        return Err(ModuleLoadError {
2061            path: module_path.to_path_buf(),
2062            kind: ModuleLoadErrorKind::UnsupportedModuleVersion(module.version),
2063        });
2064    }
2065    Ok(module)
2066}
2067
2068/// Load the current smart contract module output using the environment variable
2069/// `CARGO_CONCORDIUM_TEST_MODULE_OUTPUT_PATH` which is set when running using
2070/// `cargo concordium test`.
2071pub fn module_load_output() -> Result<WasmModule, OutputModuleLoadError> {
2072    let module_path = env::var(CONTRACT_MODULE_OUTPUT_PATH_ENV_VAR)?;
2073    let module = module_load_v1(module_path)?;
2074    Ok(module)
2075}
2076
2077impl Signer {
2078    /// Create a signer which always signs with one key.
2079    pub const fn with_one_key() -> Self {
2080        Self {
2081            num_keys: 1,
2082        }
2083    }
2084
2085    /// Create a signer with a non-zero number of keys.
2086    pub const fn with_keys(num_keys: u32) -> Result<Self, ZeroKeysError> {
2087        if num_keys == 0 {
2088            return Err(ZeroKeysError);
2089        }
2090        Ok(Self {
2091            num_keys,
2092        })
2093    }
2094}
2095
2096impl ContractInvokeError {
2097    /// Try to extract the value returned.
2098    ///
2099    /// This only returns `Some` if the contract rejected on its own.
2100    /// As opposed to when it runs out of energy, traps, or similar, in which
2101    /// case there won't be a return value.
2102    pub fn return_value(&self) -> Option<&[u8]> {
2103        match &self.kind {
2104            ContractInvokeErrorKind::ExecutionError {
2105                failure_kind:
2106                    v1::InvokeFailure::ContractReject {
2107                        data,
2108                        ..
2109                    },
2110            } => Some(data),
2111            _ => None,
2112        }
2113    }
2114
2115    /// If the contract execution rejected the transaction, this returns the
2116    /// code the contract used to signal the rejection.
2117    pub fn reject_code(&self) -> Option<i32> {
2118        match &self.kind {
2119            ContractInvokeErrorKind::ExecutionError {
2120                failure_kind:
2121                    v1::InvokeFailure::ContractReject {
2122                        code,
2123                        ..
2124                    },
2125            } => Some(*code),
2126            _ => None,
2127        }
2128    }
2129
2130    /// Try to extract and parse the value returned into a type that implements
2131    /// [`Deserial`].
2132    ///
2133    /// Returns an error if the return value:
2134    ///  - isn't present
2135    ///    - see [`Self::return_value`] for details about when this happens
2136    ///  - is present
2137    ///    - but could not be parsed into `T`
2138    ///    - could parse into `T`, but there were leftover bytes
2139    pub fn parse_return_value<T: Deserial>(&self) -> ParseResult<T> {
2140        use contracts_common::{Cursor, Get, ParseError};
2141        let return_value = self.return_value().ok_or_else(ParseError::default)?;
2142        let mut cursor = Cursor::new(return_value);
2143        let res = cursor.get()?;
2144        // Check that all bytes have been read, as leftover bytes usually indicate
2145        // errors.
2146        if cursor.offset != return_value.len() {
2147            return Err(ParseError::default());
2148        }
2149        Ok(res)
2150    }
2151}
2152
2153impl From<TestConfigurationError> for ContractInvokeErrorKind {
2154    fn from(err: TestConfigurationError) -> Self {
2155        match err {
2156            TestConfigurationError::OutOfEnergy {
2157                debug_trace,
2158            } => Self::OutOfEnergy {
2159                debug_trace,
2160            },
2161            TestConfigurationError::BalanceOverflow => Self::BalanceOverflow,
2162        }
2163    }
2164}
2165
2166/// Convert [`Energy`] to [`InterpreterEnergy`] by multiplying by `1000`.
2167pub(crate) fn to_interpreter_energy(energy: Energy) -> u64 { energy.energy * 1000 }
2168
2169/// Convert [`InterpreterEnergy`] to [`Energy`] by dividing by `1000`.
2170pub(crate) fn from_interpreter_energy(interpreter_energy: &InterpreterEnergy) -> Energy {
2171    Energy::from(interpreter_energy.energy / 1000)
2172}
2173
2174/// Calculate the energy for looking up a [`ContractModule`].
2175pub(crate) fn lookup_module_cost(module: &ContractModule) -> Energy {
2176    // The ratio is from Concordium/Cost.hs::lookupModule
2177    Energy::from(module.size / 500)
2178}
2179
2180/// Calculate the microCCD(mCCD) cost of energy(NRG) using the two exchange
2181/// rates provided.
2182///
2183/// To find the mCCD/NRG exchange rate:
2184/// ```markdown
2185///  euro     mCCD   euro * mCCD   mCCD
2186///  ----  *  ---- = ----------- = ----
2187///  NRG      euro   NRG * euro    NRG
2188/// ```
2189///
2190/// To convert the `energy` parameter to mCCD (the vertical lines represent
2191/// ceiling):
2192/// ```markdown
2193/// ⌈       mCCD  ⌉   ⌈ NRG * mCCD ⌉
2194/// | NRG * ----  | = | ---------- | = mCCD
2195/// |       NRG   |   |    NRG     |
2196/// ```
2197pub fn energy_to_amount(
2198    energy: Energy,
2199    euro_per_energy: ExchangeRate,
2200    micro_ccd_per_euro: ExchangeRate,
2201) -> Amount {
2202    let micro_ccd_per_energy_numerator: BigUint =
2203        BigUint::from(euro_per_energy.numerator()) * micro_ccd_per_euro.numerator();
2204    let micro_ccd_per_energy_denominator: BigUint =
2205        BigUint::from(euro_per_energy.denominator()) * micro_ccd_per_euro.denominator();
2206    let cost: BigUint = (micro_ccd_per_energy_numerator * energy.energy)
2207        .div_ceil(&micro_ccd_per_energy_denominator);
2208    let cost: u64 = u64::try_from(cost).expect(
2209        "Should never overflow since reasonable exchange rates are ensured when constructing the \
2210         [`Chain`].",
2211    );
2212    Amount::from_micro_ccd(cost)
2213}
2214
2215/// Helper function that checks the validity of the exchange rates.
2216///
2217/// More specifically, it checks that the cost of one energy is <= `u64::MAX /
2218/// `100_000_000_000`, which ensures that overflows won't occur.
2219fn check_exchange_rates(
2220    euro_per_energy: ExchangeRate,
2221    micro_ccd_per_euro: ExchangeRate,
2222) -> Result<(), ExchangeRateError> {
2223    let micro_ccd_per_energy_numerator: BigUint =
2224        BigUint::from(euro_per_energy.numerator()) * micro_ccd_per_euro.numerator();
2225    let micro_ccd_per_energy_denominator: BigUint =
2226        BigUint::from(euro_per_energy.denominator()) * micro_ccd_per_euro.denominator();
2227    let max_allowed_micro_ccd_to_energy = u64::MAX / 100_000_000_000u64;
2228    let micro_ccd_per_energy =
2229        u64::try_from(micro_ccd_per_energy_numerator / micro_ccd_per_energy_denominator)
2230            .map_err(|_| ExchangeRateError)?;
2231    if micro_ccd_per_energy > max_allowed_micro_ccd_to_energy {
2232        return Err(ExchangeRateError);
2233    }
2234    Ok(())
2235}
2236
2237/// A helper function for converting `[v0::Logs]` into [`Vec<ContractEvent>`].
2238pub(crate) fn contract_events_from_logs(logs: v0::Logs) -> Vec<ContractEvent> {
2239    logs.logs.into_iter().map(ContractEvent::from).collect()
2240}
2241
2242impl From<ExternalNodeNotConfigured> for ExternalNodeError {
2243    fn from(_: ExternalNodeNotConfigured) -> Self { Self::NotConfigured }
2244}
2245
2246impl From<ExchangeRateError> for ChainBuilderError {
2247    fn from(_: ExchangeRateError) -> Self { Self::ExchangeRateError }
2248}
2249
2250#[cfg(test)]
2251mod tests {
2252    use concordium_rust_sdk::base::base::AccountAddressEq;
2253
2254    use super::*;
2255
2256    /// A few checks that test whether the function behavior matches its doc
2257    /// comments.
2258    #[test]
2259    fn check_exchange_rates_works() {
2260        let max_allowed_micro_ccd_per_energy = u64::MAX / 100_000_000_000;
2261        check_exchange_rates(
2262            ExchangeRate::new_unchecked(max_allowed_micro_ccd_per_energy + 1, 1),
2263            ExchangeRate::new_unchecked(1, 1),
2264        )
2265        .expect_err("should fail");
2266
2267        check_exchange_rates(
2268            ExchangeRate::new_unchecked(max_allowed_micro_ccd_per_energy / 2 + 1, 1),
2269            ExchangeRate::new_unchecked(2, 1),
2270        )
2271        .expect_err("should fail");
2272
2273        check_exchange_rates(
2274            ExchangeRate::new_unchecked(max_allowed_micro_ccd_per_energy, 1),
2275            ExchangeRate::new_unchecked(1, 1),
2276        )
2277        .expect("should succeed");
2278
2279        check_exchange_rates(
2280            ExchangeRate::new_unchecked(50000, 1),
2281            ExchangeRate::new_unchecked(1, 50000),
2282        )
2283        .expect("should succeed");
2284    }
2285
2286    /// Test that account aliases are seen as one account.
2287    #[test]
2288    fn test_account_aliases() {
2289        let mut chain = Chain::new();
2290        let acc = AccountAddress([
2291            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2292            0, 0, 0,
2293        ]);
2294        let acc_alias = AccountAddress([
2295            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2296            1, 2, 3, // Last three bytes can differ for aliases.
2297        ]);
2298        let acc_other = AccountAddress([
2299            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
2300            2, 3, 4, // This differs on last four bytes, so it is a different account.
2301        ]);
2302        let acc_eq: AccountAddressEq = acc.into();
2303        let acc_alias_eq: AccountAddressEq = acc_alias.into();
2304        let acc_other_eq: AccountAddressEq = acc_other.into();
2305
2306        let expected_amount = Amount::from_ccd(10);
2307        let expected_amount_other = Amount::from_ccd(123);
2308
2309        chain.create_account(Account::new(acc, expected_amount));
2310        chain.create_account(Account::new(acc_other, expected_amount_other));
2311
2312        assert_eq!(acc_eq, acc_alias_eq);
2313        assert_ne!(acc_eq, acc_other_eq);
2314
2315        assert_eq!(acc_eq.cmp(&acc_alias_eq), std::cmp::Ordering::Equal);
2316        assert_eq!(acc_eq.cmp(&acc_other_eq), std::cmp::Ordering::Less);
2317
2318        assert_eq!(chain.account_balance_available(acc_alias), Some(expected_amount));
2319        assert_eq!(chain.account_balance_available(acc_other), Some(expected_amount_other));
2320    }
2321
2322    /// Test that building a chain with valid parameters succeeds.
2323    ///
2324    /// This test does *not* include external node endpoint, see
2325    /// [`test_chain_builder_with_valid_parameters_and_with_io`] for the reason.
2326    #[test]
2327    fn test_chain_builder_with_valid_parameters() {
2328        let micro_ccd_per_euro = ExchangeRate::new_unchecked(123, 1);
2329        let euro_per_energy = ExchangeRate::new_unchecked(1, 1234);
2330        let block_time = Timestamp::from_timestamp_millis(12345);
2331        let chain = Chain::builder()
2332            .micro_ccd_per_euro(micro_ccd_per_euro)
2333            .euro_per_energy(euro_per_energy)
2334            .block_time(block_time)
2335            .build()
2336            .unwrap();
2337
2338        assert_eq!(chain.micro_ccd_per_euro(), micro_ccd_per_euro);
2339        assert_eq!(chain.euro_per_energy(), euro_per_energy);
2340        assert_eq!(chain.block_time(), block_time);
2341    }
2342
2343    /// Test that building a chain with exchange rates that are out of bounds
2344    /// fails with the right error.
2345    #[test]
2346    fn test_chain_builder_with_invalid_exchange_rates() {
2347        let micro_ccd_per_euro = ExchangeRate::new_unchecked(1000000, 1);
2348        let euro_per_energy = ExchangeRate::new_unchecked(100000000, 1);
2349        let error = Chain::builder()
2350            .micro_ccd_per_euro(micro_ccd_per_euro)
2351            .euro_per_energy(euro_per_energy)
2352            .build()
2353            .unwrap_err();
2354
2355        assert!(matches!(error, ChainBuilderError::ExchangeRateError));
2356    }
2357}
2358
2359/// Return whether execution is running under `cargo concordium test` with
2360/// debugging enabled.
2361pub fn is_debug_enabled() -> bool {
2362    let Some(value) = option_env!("CARGO_CONCORDIUM_TEST_ALLOW_DEBUG") else {
2363        return false;
2364    };
2365    value != "0" && value != "false"
2366}
2367
2368/// Tests that use I/O (network) and should therefore *not* be run in the CI.
2369///
2370/// To skip the tests use `cargo test -- --skip io_tests`
2371#[cfg(test)]
2372mod io_tests {
2373    use super::*;
2374    use crate::*;
2375
2376    /// Test that building a chain using the external node parameters works.
2377    #[test]
2378    fn test_chain_builder_with_valid_parameters_and_external_node() {
2379        let chain = Chain::builder()
2380            .micro_ccd_per_euro_from_external()
2381            .euro_per_energy_from_external()
2382            .external_query_block(
2383                "45c53a19cd782a8de981941feb5e0f875cefaba8d2cda958e76f471a4710a797" // A block from testnet.
2384                    .parse()
2385                    .unwrap(),
2386            )
2387            .external_node_connection(Endpoint::from_static(
2388                "http://node.testnet.concordium.com:20000",
2389            ))
2390            .block_time_from_external()
2391            .build()
2392            .unwrap();
2393
2394        // These values are queried manually from the node.
2395        assert_eq!(
2396            chain.micro_ccd_per_euro(),
2397            ExchangeRate::new_unchecked(10338559485590134784, 79218205097)
2398        );
2399        assert_eq!(chain.euro_per_energy(), ExchangeRate::new_unchecked(1, 50000));
2400        assert_eq!(chain.block_time(), Timestamp::from_timestamp_millis(1687865059500));
2401    }
2402
2403    /// Test that the correct error is returned when an unknown query block is
2404    /// given.
2405    ///
2406    /// The block used is one from mainnet, which is extremely unlikely to also
2407    /// appear on testnet.
2408    #[test]
2409    fn test_block_time_from_unknown_block() {
2410        let err =
2411            Chain::builder()
2412                .external_node_connection(Endpoint::from_static(
2413                    "http://node.testnet.concordium.com:20000",
2414                ))
2415                .external_query_block(
2416                    "4f38c7e63645c59e9bf32f7ca837a029810b21c439f7492c3cebe229a2e3ea07"
2417                        .parse()
2418                        .unwrap(), // A block from mainnet.
2419                )
2420                .build()
2421                .unwrap_err();
2422        assert!(matches!(err, ChainBuilderError::SetupExternalNodeError {
2423            error: SetupExternalNodeError::CannotCheckQueryBlockExistence { .. },
2424        }));
2425    }
2426
2427    /// Invoke an external contract and check that it succeeds. Also check that
2428    /// the energy is correct.
2429    #[test]
2430    fn test_contract_invoke_external() {
2431        let mut chain = Chain::builder()
2432            .external_node_connection(Endpoint::from_static(
2433                "http://node.testnet.concordium.com:20000",
2434            ))
2435            .build()
2436            .unwrap();
2437
2438        // A CIS-2 contract.
2439        let external_contr = chain.add_external_contract(ContractAddress::new(5089, 0)).unwrap();
2440
2441        let external_acc = chain
2442            .add_external_account(
2443                "3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini".parse().unwrap(),
2444            )
2445            .unwrap();
2446
2447        let res = chain
2448            .contract_invoke_external(
2449                Some(external_acc.into()),
2450                10000.into(),
2451                InvokeExternalContractPayload {
2452                    amount:       Amount::zero(),
2453                    address:      external_contr,
2454                    receive_name: OwnedReceiveName::new_unchecked("cis2_multi.view".into()),
2455                    message:      OwnedParameter::empty(),
2456                },
2457                None,
2458            )
2459            .unwrap();
2460        assert_eq!(res.energy_used, 1851.into());
2461    }
2462}