concordium_smart_contract_testing/invocation/
impls.rs

1use super::types::*;
2use crate::{
3    constants::{self, verify_ed25519_energy_cost},
4    impls::{
5        contract_events_from_logs, from_interpreter_energy, lookup_module_cost,
6        to_interpreter_energy,
7    },
8    types::{Account, BalanceError, Contract, ContractModule, TransferError},
9    AccountSignatures, DebugTraceElement, ExecutionError, InvokeExecutionError,
10};
11use concordium_rust_sdk::{
12    base::{
13        self,
14        base::{AccountAddressEq, Energy, InsufficientEnergy},
15        common,
16        contracts_common::{
17            to_bytes, AccountAddress, AccountBalance, Address, Amount, ChainMetadata,
18            ContractAddress, ExchangeRates, ModuleReference, OwnedEntrypointName, OwnedReceiveName,
19        },
20        smart_contracts::{
21            ContractTraceElement, InstanceUpdatedEvent, OwnedContractName, OwnedParameter,
22            WasmVersion,
23        },
24        transactions::{verify_data_signature, AccountAccessStructure, UpdateContractPayload},
25    },
26    smart_contracts::engine::{
27        v0,
28        v1::{self, trie, DebugTracker, InvokeResponse},
29        wasm::artifact::{self, CompiledFunction},
30        DebugInfo, InterpreterEnergy,
31    },
32    types::smart_contracts::ContractName,
33};
34use std::collections::{btree_map, BTreeMap};
35
36// Exit early with an out of energy error.
37macro_rules! exit_ooe {
38    ($charge:expr, $trace:expr) => {
39        if let Err(InsufficientEnergy) = $charge {
40            return Err(TestConfigurationError::OutOfEnergy {
41                debug_trace: $trace,
42            });
43        }
44    };
45}
46
47impl InvocationData {
48    /// An internal helper function to construct a [`DebugTraceElement`] from a
49    /// set of debug events.
50    pub(crate) fn debug_trace(&self, debug_trace: DebugTracker) -> DebugTraceElement {
51        DebugTraceElement::Debug {
52            entrypoint: self.entrypoint.clone(),
53            address: self.address,
54            debug_trace,
55        }
56    }
57}
58
59/// Ok response from the `invoke_entrypoint_initial` method. This is the
60/// execution up to either success, failure, or the first interrupt.
61///
62/// The response is rather complex. In case we could not even run the contract
63/// because the preconditions failed (such as not enough balance on some account
64/// or contract) the response is the `Err` variant.
65///
66/// If we managed to get to running the contract entrypoint then the response is
67/// the `Ok` variant, with more detailed information inside.
68type InitialInvokeResponse =
69    Result<(v1::ReceiveResult<CompiledFunction, DebugTracker>, InvocationData), InvokeResponse>;
70
71impl<'a, 'b> EntrypointInvocationHandler<'a, 'b> {
72    /// Used for handling the *initial* part of invoking an entrypoint.
73    ///
74    /// **Preconditions:**
75    ///  - `invoker` exists
76    ///  - `invoker` has sufficient balance to pay for `remaining_energy`
77    ///  - `sender` exists
78    ///  - if the contract (`contract_address`) exists, then its `module` must
79    ///    also exist.
80    fn invoke_entrypoint_initial(
81        &mut self,
82        invoker: AccountAddress,
83        sender: Address,
84        payload: UpdateContractPayload,
85        trace_elements_checkpoint: usize,
86    ) -> Result<InitialInvokeResponse, TestConfigurationError> {
87        // Charge the base cost for updating a contract.
88        exit_ooe!(
89            self.remaining_energy.tick_energy(constants::UPDATE_CONTRACT_INSTANCE_BASE_COST),
90            DebugTracker::empty_trace()
91        );
92
93        // Move the amount from the sender to the contract, if any.
94        // And get the new self_balance.
95        let instance_self_balance = if payload.amount.micro_ccd() > 0 {
96            let transfer_result = match sender {
97                Address::Account(sender_account) => self.transfer_from_account_to_contract(
98                    payload.amount,
99                    sender_account,
100                    payload.address,
101                ),
102                Address::Contract(sender_contract) => self.transfer_from_contract_to_contract(
103                    payload.amount,
104                    sender_contract,
105                    payload.address,
106                ),
107            };
108            match transfer_result {
109                Ok(new_balance_from) => new_balance_from,
110                Err(transfer_error) => {
111                    let kind = match transfer_error {
112                        TransferError::BalanceError {
113                            error: BalanceError::Overflow,
114                        } => {
115                            // Balance overflows are unrecoverable and short circuit.
116                            return Err(TestConfigurationError::BalanceOverflow);
117                        }
118                        TransferError::BalanceError {
119                            error: BalanceError::Insufficient,
120                        } => v1::InvokeFailure::InsufficientAmount,
121                        TransferError::ToMissing => v1::InvokeFailure::NonExistentContract,
122                    };
123                    // Return early.
124                    return Ok(Err(v1::InvokeResponse::Failure {
125                        kind,
126                    }));
127                }
128            }
129        } else {
130            match self.contract_balance(payload.address) {
131                Some(self_balance) => self_balance,
132                None => {
133                    // Return early.
134                    return Ok(Err(v1::InvokeResponse::Failure {
135                        kind: v1::InvokeFailure::NonExistentContract,
136                    }));
137                }
138            }
139        };
140
141        // Get the instance and artifact. To be used in several places.
142        let instance = self
143            .chain
144            .contracts
145            .get(&payload.address)
146            .expect("Contract known to exist at this point");
147        let module = self.contract_module(instance);
148
149        // Construct the receive name (or fallback receive name) and ensure its presence
150        // in the contract. Also returns the contract name and entrypoint name as they
151        // are needed further down.
152        let (contract_name, receive_name, entrypoint_name) = {
153            let borrowed_receive_name = payload.receive_name.as_receive_name();
154            let contract_name = borrowed_receive_name.contract_name();
155            let owned_contract_name =
156                OwnedContractName::new_unchecked(format!("init_{}", contract_name));
157            let owned_entrypoint_name = borrowed_receive_name.entrypoint_name().to_owned();
158            let receive_name = borrowed_receive_name.get_chain_name();
159            let fallback_receive_name = format!("{}.", contract_name);
160            if module.artifact.has_entrypoint(receive_name) {
161                (owned_contract_name, payload.receive_name, owned_entrypoint_name)
162            } else if module.artifact.has_entrypoint(fallback_receive_name.as_str()) {
163                (
164                    owned_contract_name,
165                    OwnedReceiveName::new_unchecked(fallback_receive_name),
166                    owned_entrypoint_name,
167                )
168            } else {
169                // Return early.
170                return Ok(Err(v1::InvokeResponse::Failure {
171                    kind: v1::InvokeFailure::NonExistentEntrypoint,
172                }));
173            }
174        };
175
176        // Subtract the cost of looking up the module
177        let lookup_costs = lookup_module_cost(&module);
178        exit_ooe!(self.remaining_energy.tick_energy(lookup_costs), DebugTracker::empty_trace());
179        self.module_load_energy.energy += lookup_costs.energy;
180
181        // Sender policies have a very bespoke serialization in
182        // order to allow skipping portions of them in smart contracts.
183        let sender_policies = {
184            let mut out = Vec::new();
185            let policy = &self
186                .chain
187                .account(invoker)
188                .expect("Precondition violation: invoker must exist.")
189                .policy;
190            policy
191                .serial_for_smart_contract(&mut out)
192                .expect("Writing to a vector should succeed.");
193            out
194        };
195
196        // Construct the receive context
197        let receive_ctx = v1::ReceiveContext {
198            // This should be the entrypoint specified, even if we end up
199            // calling the fallback entrypoint, as this will be visible to the
200            // contract via a host function.
201            entrypoint: entrypoint_name.clone(),
202            common:     v0::ReceiveContext {
203                metadata: ChainMetadata {
204                    slot_time: self.chain.parameters.block_time,
205                },
206                invoker,
207                self_address: payload.address,
208                self_balance: instance_self_balance,
209                sender,
210                owner: instance.owner,
211                sender_policies,
212            },
213        };
214
215        let mod_idx_before_invoke = self.next_contract_modification_index;
216
217        // Construct the instance state
218        let mut loader = v1::trie::Loader::new(&[][..]); // An empty loader is fine currently, as we do not use caching in this lib.
219        let mut mutable_state = self.contract_state(payload.address);
220        let mut mutable_state = mutable_state.make_fresh_generation(&mut loader);
221        let inner = mutable_state.get_inner(&mut loader);
222        let instance_state = v1::InstanceState::new(loader, inner);
223
224        // Get the initial result from invoking receive
225        let initial_result = self.run_interpreter(|energy| {
226            v1::invoke_receive(
227                module.artifact,
228                receive_ctx,
229                v1::ReceiveInvocation {
230                    amount: payload.amount,
231                    // This will either be the one provided on the fallback receive name.
232                    receive_name: receive_name.as_receive_name(),
233                    parameter: payload.message.as_ref(),
234                    energy,
235                },
236                instance_state,
237                v1::ReceiveParams::new_p7(),
238            )
239        })?;
240        // Set up some data needed for recursively processing the receive until the end,
241        // i.e. beyond interrupts.
242        Ok(Ok((initial_result, InvocationData {
243            sender,
244            address: payload.address,
245            contract_name,
246            entrypoint: entrypoint_name,
247            parameter: payload.message,
248            amount: payload.amount,
249            state: mutable_state,
250            trace_elements_checkpoint,
251            next_mod_idx_checkpoint: mod_idx_before_invoke,
252            mod_idx_before_invoke,
253        })))
254    }
255
256    /// Used for handling contract entrypoint invocations internally.
257    ///
258    /// **Preconditions:**
259    ///  - `invoker` exists
260    ///  - `invoker` has sufficient balance to pay for `remaining_energy`
261    ///  - `sender` exists
262    ///  - if the contract (`contract_address`) exists, then its `module` must
263    ///    also exist.
264    pub(crate) fn invoke_entrypoint(
265        &mut self,
266        invoker: AccountAddress,
267        sender: Address,
268        payload: UpdateContractPayload,
269    ) -> Result<(InvokeResponse, Vec<DebugTraceElement>), TestConfigurationError> {
270        let mut stack = Vec::new();
271        let mut trace_elements = Vec::new();
272        stack.push(Next::Initial {
273            sender,
274            payload,
275            trace_elements_checkpoint: 0,
276        });
277        // Initialized to a dummy value. This will always be set or the function will
278        // terminate with an Err.
279        let mut invoke_response: Option<InvokeResponse> = None;
280        while let Some(invocation_data) = stack.pop() {
281            let (receive_result, mut invocation_data) = match invocation_data {
282                Next::Resume {
283                    mut data,
284                    config,
285                    response,
286                } => {
287                    match response {
288                        Some(response) => {
289                            let receive_result = self.run_interpreter(|energy| {
290                                v1::resume_receive(
291                                    config,
292                                    response,
293                                    energy,
294                                    &mut data.state,
295                                    false, /* the state never changes on interrupts that have
296                                            * immediate handlers */
297                                    // An empty loader is fine currently, as we do not use
298                                    // caching in this lib.
299                                    v1::trie::Loader::new(&[][..]),
300                                )
301                            })?;
302                            (receive_result, data)
303                        }
304                        None => {
305                            // we are resuming from a contract call
306                            let (success, call_response) = match invoke_response
307                                .take()
308                                .expect("Response should be available")
309                            {
310                                v1::InvokeResponse::Success {
311                                    data: return_value,
312                                    ..
313                                } => {
314                                    let invoke_response = v1::InvokeResponse::Success {
315                                        // The balance returned by `invoke_entrypoint`
316                                        // is the balance of the contract called. But we
317                                        // are interested in the new balance of the caller.
318                                        new_balance: self.contract_balance_unchecked(data.address),
319                                        data:        return_value,
320                                    };
321                                    (true, invoke_response)
322                                }
323                                failure => (false, failure),
324                            };
325
326                            // Remove the last state changes if the invocation failed.
327                            let state_changed = if !success {
328                                self.rollback();
329                                false // We rolled back, so no changes were
330                                      // made to this contract.
331                            } else {
332                                let mod_idx_after_invoke = self.modification_index(data.address);
333                                let state_changed =
334                                    mod_idx_after_invoke != data.mod_idx_before_invoke;
335                                if state_changed {
336                                    // Update the state field with the newest value from the
337                                    // changeset.
338                                    data.state = self.contract_state(data.address);
339                                }
340                                state_changed
341                            };
342
343                            // Add resume event
344                            let resume_event = ContractTraceElement::Resumed {
345                                address: data.address,
346                                success,
347                            };
348
349                            self.push_regular_trace_element(
350                                &mut trace_elements,
351                                resume_event,
352                                data.entrypoint.clone(),
353                                DebugTracker::empty_trace(), /* Resume events do not have any
354                                                              * debug trace. */
355                            );
356                            let receive_result = self.run_interpreter(|energy| {
357                                v1::resume_receive(
358                                    config,
359                                    call_response,
360                                    energy,
361                                    &mut data.state,
362                                    state_changed,
363                                    // An empty loader is fine currently, as we do not use
364                                    // caching in this lib.
365                                    v1::trie::Loader::new(&[][..]),
366                                )
367                            })?;
368                            (receive_result, data)
369                        }
370                    }
371                }
372                Next::Initial {
373                    sender,
374                    payload,
375                    trace_elements_checkpoint,
376                } => {
377                    match self.invoke_entrypoint_initial(
378                        invoker,
379                        sender,
380                        payload,
381                        trace_elements_checkpoint,
382                    )? {
383                        Ok(x) => x,
384                        Err(ier) => {
385                            // invocation has failed. No more to do for this call.
386                            // Since no traces were produced we don't have to roll them back.
387                            invoke_response = Some(ier);
388                            continue;
389                        }
390                    }
391                }
392            };
393
394            match receive_result {
395                v1::ReceiveResult::Success {
396                    logs,
397                    state_changed,
398                    return_value,
399                    remaining_energy,
400                    trace,
401                } => {
402                    // Update the remaining_energy field.
403                    self.update_energy(remaining_energy);
404
405                    let update_event = ContractTraceElement::Updated {
406                        data: InstanceUpdatedEvent {
407                            contract_version: WasmVersion::V1,
408                            address:          invocation_data.address,
409                            instigator:       invocation_data.sender,
410                            amount:           invocation_data.amount,
411                            message:          invocation_data.parameter.clone(),
412                            receive_name:     OwnedReceiveName::construct_unchecked(
413                                invocation_data.contract_name.as_contract_name(),
414                                invocation_data.entrypoint.as_entrypoint_name(),
415                            ),
416                            events:           contract_events_from_logs(logs),
417                        },
418                    };
419                    // Add update event
420                    self.push_regular_trace_element(
421                        &mut trace_elements,
422                        update_event,
423                        invocation_data.entrypoint.clone(),
424                        trace,
425                    );
426
427                    // Save changes to changeset.
428                    if state_changed {
429                        self.save_state_changes(
430                            invocation_data.address,
431                            &mut invocation_data.state,
432                        );
433                    }
434
435                    invoke_response = Some(v1::InvokeResponse::Success {
436                        new_balance: self.contract_balance_unchecked(invocation_data.address),
437                        data:        Some(return_value),
438                    });
439                }
440                v1::ReceiveResult::Interrupt {
441                    remaining_energy,
442                    state_changed,
443                    logs,
444                    config,
445                    interrupt,
446                    trace,
447                } => {
448                    // Update the remaining_energy field.
449                    self.update_energy(remaining_energy);
450                    // Create the interrupt event, which will be included for transfers, calls,
451                    // and upgrades, but not for the remaining
452                    // interrupts.
453                    let interrupt_event = ContractTraceElement::Interrupted {
454                        address: invocation_data.address,
455                        events:  contract_events_from_logs(logs),
456                    };
457                    // Remember what state we are in before invoking.
458                    // This is used to report, upon resume, whether the contracts's
459                    // state has changed.
460                    invocation_data.mod_idx_before_invoke = if state_changed {
461                        self.save_state_changes(invocation_data.address, &mut invocation_data.state)
462                    } else {
463                        self.modification_index(invocation_data.address)
464                    };
465                    match interrupt {
466                        v1::Interrupt::Transfer {
467                            to,
468                            amount,
469                        } => {
470                            // Add the interrupt event
471                            self.push_regular_trace_element(
472                                &mut trace_elements,
473                                interrupt_event,
474                                invocation_data.entrypoint.clone(),
475                                trace,
476                            );
477
478                            let response = match self.transfer_from_contract_to_account(
479                                amount,
480                                invocation_data.address,
481                                to,
482                            ) {
483                                Ok(new_balance) => v1::InvokeResponse::Success {
484                                    new_balance,
485                                    data: None,
486                                },
487                                Err(err) => {
488                                    let kind = match err {
489                                        TransferError::ToMissing => {
490                                            v1::InvokeFailure::NonExistentAccount
491                                        }
492
493                                        TransferError::BalanceError {
494                                            error: BalanceError::Insufficient,
495                                        } => v1::InvokeFailure::InsufficientAmount,
496
497                                        TransferError::BalanceError {
498                                            error: BalanceError::Overflow,
499                                        } => {
500                                            // Balance overflows are unrecoverable and short
501                                            // circuit.
502                                            return Err(TestConfigurationError::BalanceOverflow);
503                                        }
504                                    };
505                                    v1::InvokeResponse::Failure {
506                                        kind,
507                                    }
508                                }
509                            };
510
511                            let success = matches!(response, v1::InvokeResponse::Success { .. });
512                            if success {
513                                // Add transfer event
514                                let transfer_event = ContractTraceElement::Transferred {
515                                    from: invocation_data.address,
516                                    amount,
517                                    to,
518                                };
519                                self.push_regular_trace_element(
520                                    &mut trace_elements,
521                                    transfer_event,
522                                    invocation_data.entrypoint.clone(),
523                                    DebugTracker::empty_trace(), // No trace for transfer events.
524                                );
525                            }
526                            // Add resume event
527                            let resume_event = ContractTraceElement::Resumed {
528                                address: invocation_data.address,
529                                success,
530                            };
531                            self.push_regular_trace_element(
532                                &mut trace_elements,
533                                resume_event,
534                                invocation_data.entrypoint.clone(),
535                                DebugTracker::empty_trace(), /* Resume events don't have a new
536                                                              * trace on their own. */
537                            );
538
539                            exit_ooe!(
540                                self.remaining_energy
541                                    .tick_energy(base::transactions::cost::SIMPLE_TRANSFER),
542                                DebugTracker::empty_trace()
543                            );
544
545                            stack.push(Next::Resume {
546                                data: invocation_data,
547                                // the state never changes on transfers
548                                config,
549                                response: Some(response),
550                            });
551                        }
552                        v1::Interrupt::Call {
553                            address,
554                            parameter,
555                            name,
556                            amount,
557                        } => {
558                            // Add the interrupt event
559                            self.push_regular_trace_element(
560                                &mut trace_elements,
561                                interrupt_event,
562                                invocation_data.entrypoint.clone(),
563                                trace,
564                            );
565
566                            match self
567                                .chain
568                                .contracts
569                                .get(&address)
570                                .map(|c| c.contract_name.as_contract_name())
571                            {
572                                // The contract to call does not exist.
573                                None => {
574                                    let response = v1::InvokeResponse::Failure {
575                                        kind: v1::InvokeFailure::NonExistentContract,
576                                    };
577                                    // Add resume event
578                                    let resume_event = ContractTraceElement::Resumed {
579                                        address: invocation_data.address,
580                                        success: false,
581                                    };
582                                    self.push_regular_trace_element(
583                                        &mut trace_elements,
584                                        resume_event,
585                                        invocation_data.entrypoint.clone(),
586                                        DebugTracker::empty_trace(), /* Nothing happened since
587                                                                      * the contract does not
588                                                                      * exist. */
589                                    );
590                                    stack.push(Next::Resume {
591                                        data: invocation_data,
592                                        config,
593                                        response: Some(response),
594                                    });
595                                }
596                                Some(contract_name) => {
597                                    // Make a checkpoint before calling another contract so that we
598                                    // may roll back.
599                                    self.checkpoint();
600
601                                    let receive_name = OwnedReceiveName::construct_unchecked(
602                                        contract_name,
603                                        name.as_entrypoint_name(),
604                                    );
605                                    let message = OwnedParameter::new_unchecked(parameter);
606
607                                    let sender = Address::Contract(invocation_data.address);
608                                    // Remember to continue the current execution after handling the
609                                    // call.
610                                    stack.push(Next::Resume {
611                                        data: invocation_data,
612                                        config,
613                                        response: None,
614                                    });
615
616                                    // Add the call to the stack to execute.
617                                    stack.push(Next::Initial {
618                                        sender,
619                                        payload: UpdateContractPayload {
620                                            amount,
621                                            address,
622                                            receive_name,
623                                            message,
624                                        },
625                                        trace_elements_checkpoint: trace_elements.len(),
626                                    });
627                                }
628                            };
629                        }
630                        v1::Interrupt::Upgrade {
631                            module_ref,
632                        } => {
633                            // Add the interrupt event.
634                            self.push_regular_trace_element(
635                                &mut trace_elements,
636                                interrupt_event,
637                                invocation_data.entrypoint.clone(),
638                                trace,
639                            );
640
641                            // Charge a base cost.
642                            exit_ooe!(
643                                self.remaining_energy
644                                    .tick_energy(constants::INITIALIZE_CONTRACT_INSTANCE_BASE_COST),
645                                DebugTracker::empty_trace()
646                            );
647
648                            let response =
649                                match self.chain.modules.get(&module_ref) {
650                                    None => v1::InvokeResponse::Failure {
651                                        kind: v1::InvokeFailure::UpgradeInvalidModuleRef,
652                                    },
653                                    Some(module) => {
654                                        // Charge for the module lookup.
655                                        let lookup_costs = lookup_module_cost(module);
656                                        self.module_load_energy.energy += lookup_costs.energy;
657                                        exit_ooe!(
658                                            self.remaining_energy.tick_energy(lookup_costs),
659                                            DebugTracker::empty_trace()
660                                        );
661
662                                        if module.artifact.export.contains_key(
663                                            invocation_data
664                                                .contract_name
665                                                .as_contract_name()
666                                                .get_chain_name(),
667                                        ) {
668                                            // Update module reference in the changeset.
669                                            let old_module_ref = self.save_module_upgrade(
670                                                invocation_data.address,
671                                                module_ref,
672                                            );
673
674                                            // Charge for the initialization cost.
675                                            exit_ooe!(self.remaining_energy.tick_energy(
676                                                constants::INITIALIZE_CONTRACT_INSTANCE_CREATE_COST,
677                                            ), DebugTracker::empty_trace());
678
679                                            let upgrade_event = ContractTraceElement::Upgraded {
680                                                address: invocation_data.address,
681                                                from:    old_module_ref,
682                                                to:      module_ref,
683                                            };
684
685                                            self.push_regular_trace_element(
686                                                &mut trace_elements,
687                                                upgrade_event,
688                                                invocation_data.entrypoint.clone(),
689                                                DebugTracker::empty_trace(), /* Nothing happened
690                                                                              * in
691                                                                              * between. */
692                                            );
693
694                                            v1::InvokeResponse::Success {
695                                                new_balance: self.contract_balance_unchecked(
696                                                    invocation_data.address,
697                                                ),
698                                                data:        None,
699                                            }
700                                        } else {
701                                            v1::InvokeResponse::Failure {
702                                                kind: v1::InvokeFailure::UpgradeInvalidContractName,
703                                            }
704                                        }
705                                    }
706                                };
707
708                            let success = matches!(response, v1::InvokeResponse::Success { .. });
709                            let resumed_event = ContractTraceElement::Resumed {
710                                address: invocation_data.address,
711                                success,
712                            };
713                            self.push_regular_trace_element(
714                                &mut trace_elements,
715                                resumed_event,
716                                invocation_data.entrypoint.clone(),
717                                DebugTracker::empty_trace(),
718                            );
719                            stack.push(Next::Resume {
720                                data: invocation_data,
721                                config,
722                                response: Some(response),
723                            });
724                        }
725                        v1::Interrupt::QueryAccountBalance {
726                            address,
727                        } => {
728                            let response = match self.account_balance(address) {
729                                Some(balance) => v1::InvokeResponse::Success {
730                                    new_balance: self
731                                        .contract_balance_unchecked(invocation_data.address),
732                                    data:        Some(to_bytes(&balance)),
733                                },
734                                None => v1::InvokeResponse::Failure {
735                                    kind: v1::InvokeFailure::NonExistentAccount,
736                                },
737                            };
738                            exit_ooe!(
739                                self.remaining_energy.tick_energy(
740                                    constants::CONTRACT_INSTANCE_QUERY_ACCOUNT_BALANCE_COST,
741                                ),
742                                trace
743                            );
744                            trace_elements.push(invocation_data.debug_trace(trace));
745                            stack.push(Next::Resume {
746                                data: invocation_data,
747                                config,
748                                response: Some(response),
749                            });
750                        }
751                        v1::Interrupt::QueryContractBalance {
752                            address,
753                        } => {
754                            let response = match self.contract_balance(address) {
755                                None => v1::InvokeResponse::Failure {
756                                    kind: v1::InvokeFailure::NonExistentContract,
757                                },
758                                Some(bal) => v1::InvokeResponse::Success {
759                                    // Balance of contract querying. Won't change. Notice the
760                                    // `invocation_data.address`.
761                                    new_balance: self
762                                        .contract_balance_unchecked(invocation_data.address),
763                                    data:        Some(to_bytes(&bal)),
764                                },
765                            };
766
767                            exit_ooe!(
768                                self.remaining_energy.tick_energy(
769                                    constants::CONTRACT_INSTANCE_QUERY_CONTRACT_BALANCE_COST,
770                                ),
771                                trace
772                            );
773                            trace_elements.push(invocation_data.debug_trace(trace));
774                            stack.push(Next::Resume {
775                                data: invocation_data,
776                                config,
777                                response: Some(response),
778                            });
779                        }
780                        v1::Interrupt::QueryExchangeRates => {
781                            let exchange_rates = ExchangeRates {
782                                euro_per_energy:    self.chain.parameters.euro_per_energy,
783                                micro_ccd_per_euro: self.chain.parameters.micro_ccd_per_euro,
784                            };
785
786                            let response = v1::InvokeResponse::Success {
787                                new_balance: self
788                                    .contract_balance_unchecked(invocation_data.address),
789                                data:        Some(to_bytes(&exchange_rates)),
790                            };
791
792                            exit_ooe!(
793                                self.remaining_energy.tick_energy(
794                                    constants::CONTRACT_INSTANCE_QUERY_EXCHANGE_RATE_COST,
795                                ),
796                                trace
797                            );
798
799                            trace_elements.push(invocation_data.debug_trace(trace));
800                            stack.push(Next::Resume {
801                                data: invocation_data,
802                                config,
803                                response: Some(response),
804                            });
805                        }
806                        v1::Interrupt::CheckAccountSignature {
807                            address,
808                            payload,
809                        } => {
810                            exit_ooe!(
811                                self.remaining_energy.tick_energy(
812                                    constants::CONTRACT_INSTANCE_QUERY_ACCOUNT_KEYS_BASE_COST,
813                                ),
814                                trace
815                            );
816                            // Due to borrow checker limitations we don't use self.account_keys here
817                            // since it leads to clashes with the mutable borrow of
818                            // self.remaining_energy below.
819                            let response = match self
820                                .chain
821                                .accounts
822                                .get(&address.into())
823                                .map(|a| &a.keys)
824                            {
825                                Some(keys) => {
826                                    // attempt to deserialize the payload after
827                                    // looking up the account.
828                                    // This is the order in the scheduler as
829                                    // well, and the order matters
830                                    // since the response to the contract is
831                                    // different depending on failure kind.
832                                    match deserial_signature_and_data_from_contract(&payload) {
833                                        Ok((sigs, data)) => {
834                                            let num_sigs = sigs.num_signatures();
835                                            exit_ooe!(
836                                                self.remaining_energy.tick_energy(
837                                                    // This cannot overflow on any data that can be
838                                                    // supplied.
839                                                    // Data_len will always be at most u32, and the
840                                                    // number of
841                                                    // signatures is at most 256*256.
842                                                    verify_ed25519_energy_cost(
843                                                        num_sigs,
844                                                        data.len() as u32,
845                                                    ),
846                                                ),
847                                                trace
848                                            );
849                                            if verify_data_signature(keys, data, &sigs.into()) {
850                                                v1::InvokeResponse::Success {
851                                                    new_balance: self.contract_balance_unchecked(
852                                                        invocation_data.address,
853                                                    ),
854                                                    data:        None,
855                                                }
856                                            } else {
857                                                v1::InvokeResponse::Failure {
858                                                    kind: v1::InvokeFailure::SignatureCheckFailed,
859                                                }
860                                            }
861                                        }
862                                        Err(_) => v1::InvokeResponse::Failure {
863                                            kind: v1::InvokeFailure::SignatureDataMalformed,
864                                        },
865                                    }
866                                }
867                                None => v1::InvokeResponse::Failure {
868                                    kind: v1::InvokeFailure::NonExistentAccount,
869                                },
870                            };
871                            trace_elements.push(invocation_data.debug_trace(trace));
872                            stack.push(Next::Resume {
873                                data: invocation_data,
874                                config,
875                                response: Some(response),
876                            });
877                        }
878                        v1::Interrupt::QueryAccountKeys {
879                            address,
880                        } => {
881                            exit_ooe!(
882                                self.remaining_energy.tick_energy(
883                                    constants::CONTRACT_INSTANCE_QUERY_ACCOUNT_KEYS_BASE_COST,
884                                ),
885                                trace
886                            );
887                            let response = match self.account_keys(address) {
888                                Some(keys) => {
889                                    let response_data = common::to_bytes(&keys);
890                                    let num_keys = keys.num_keys();
891                                    exit_ooe!(self.remaining_energy.tick_energy(
892                                        constants::contract_instance_query_account_keys_return_cost(
893                                            num_keys,
894                                        ),
895                                    ), trace);
896                                    v1::InvokeResponse::Success {
897                                        // Balance of contract querying. Does not change for this
898                                        // request.
899                                        new_balance: self
900                                            .contract_balance_unchecked(invocation_data.address),
901                                        data:        Some(response_data),
902                                    }
903                                }
904                                None => v1::InvokeResponse::Failure {
905                                    kind: v1::InvokeFailure::NonExistentAccount,
906                                },
907                            };
908                            trace_elements.push(invocation_data.debug_trace(trace));
909                            stack.push(Next::Resume {
910                                data: invocation_data,
911                                config,
912                                response: Some(response),
913                            });
914                        }
915                        v1::Interrupt::QueryContractModuleReference {
916                            address,
917                        } => {
918                            let response = match self.contract_module_reference(address) {
919                                None => v1::InvokeResponse::Failure {
920                                    kind: v1::InvokeFailure::NonExistentContract,
921                                },
922                                Some(mod_ref) => v1::InvokeResponse::Success {
923                                    // The balance of the calling contract is unchanged.
924                                    new_balance: self
925                                        .contract_balance_unchecked(invocation_data.address),
926                                    data:        Some(to_bytes(&mod_ref)),
927                                },
928                            };
929
930                            exit_ooe!(
931                                self.remaining_energy.tick_energy(
932                                    constants::CONTRACT_INSTANCE_QUERY_CONTRACT_MODULE_REFERENCE_COST,
933                                ),
934                                trace
935                            );
936                            trace_elements.push(invocation_data.debug_trace(trace));
937                            stack.push(Next::Resume {
938                                data: invocation_data,
939                                config,
940                                response: Some(response),
941                            });
942                        }
943                        v1::Interrupt::QueryContractName {
944                            address,
945                        } => {
946                            let response = match self.contract_name(address) {
947                                None => v1::InvokeResponse::Failure {
948                                    kind: v1::InvokeFailure::NonExistentContract,
949                                },
950                                Some(cname) => v1::InvokeResponse::Success {
951                                    // The balance of the calling contract is unchanged.
952                                    new_balance: self
953                                        .contract_balance_unchecked(invocation_data.address),
954                                    data:        Some(cname.get_chain_name().into()),
955                                },
956                            };
957
958                            exit_ooe!(
959                                self.remaining_energy.tick_energy(
960                                    constants::CONTRACT_INSTANCE_QUERY_CONTRACT_NAME_COST,
961                                ),
962                                trace
963                            );
964                            trace_elements.push(invocation_data.debug_trace(trace));
965                            stack.push(Next::Resume {
966                                data: invocation_data,
967                                config,
968                                response: Some(response),
969                            });
970                        }
971                    }
972                }
973                v1::ReceiveResult::Reject {
974                    reason,
975                    return_value,
976                    remaining_energy,
977                    trace,
978                } => {
979                    self.update_energy(remaining_energy);
980                    // Remove the failure stack traces from the list and include them in a failure
981                    // element.
982                    let failure_traces =
983                        trace_elements.split_off(invocation_data.trace_elements_checkpoint);
984
985                    // Create a `WithFailures` element even if `failure_traces` is empty, as the
986                    // reject reason and energy usage is still relevant.
987                    let with_failure = DebugTraceElement::WithFailures {
988                        contract_address: invocation_data.address,
989                        entrypoint:       invocation_data.entrypoint.clone(),
990                        error:            InvokeExecutionError::Reject {
991                            reason,
992                            return_value: return_value.clone(),
993                        },
994                        trace_elements:   failure_traces,
995                        energy_used:      self.energy_used(),
996                        debug_trace:      trace,
997                    };
998                    trace_elements.push(with_failure);
999
1000                    // Reset the next modification index as well.
1001                    self.next_contract_modification_index = invocation_data.next_mod_idx_checkpoint;
1002                    invoke_response = Some(v1::InvokeResponse::Failure {
1003                        kind: v1::InvokeFailure::ContractReject {
1004                            code: reason,
1005                            data: return_value,
1006                        },
1007                    });
1008                }
1009                v1::ReceiveResult::Trap {
1010                    error,
1011                    remaining_energy,
1012                    trace,
1013                } => {
1014                    self.update_energy(remaining_energy);
1015                    // Remove the failure stack traces from the list and include them in a failure
1016                    // element.
1017                    let failure_traces =
1018                        trace_elements.split_off(invocation_data.trace_elements_checkpoint);
1019
1020                    // Create a `WithFailures` element even if `failure_traces` is empty, as the
1021                    // error and energy usage is still relevant.
1022                    let with_failure = DebugTraceElement::WithFailures {
1023                        contract_address: invocation_data.address,
1024                        entrypoint:       invocation_data.entrypoint.clone(),
1025                        error:            InvokeExecutionError::Trap {
1026                            error: ExecutionError(error),
1027                        },
1028                        trace_elements:   failure_traces,
1029                        energy_used:      self.energy_used(),
1030                        debug_trace:      trace,
1031                    };
1032                    trace_elements.push(with_failure);
1033
1034                    // Reset the next modification index as well.
1035                    self.next_contract_modification_index = invocation_data.next_mod_idx_checkpoint;
1036                    invoke_response = Some(v1::InvokeResponse::Failure {
1037                        kind: v1::InvokeFailure::RuntimeError,
1038                    });
1039                }
1040                // Convert this to an error here, so that we will short circuit processing.
1041                v1::ReceiveResult::OutOfEnergy {
1042                    trace,
1043                } => {
1044                    return Err(TestConfigurationError::OutOfEnergy {
1045                        debug_trace: trace,
1046                    })
1047                }
1048            }
1049        }
1050        Ok((invoke_response.expect("Response should have been set."), trace_elements))
1051    }
1052
1053    /// Make a transfer from a contract to an account in the changeset.
1054    ///
1055    /// Returns the new balance of `from`.
1056    ///
1057    /// **Preconditions:**
1058    ///  - Assumes that `from` contract exists.
1059    fn transfer_from_contract_to_account(
1060        &mut self,
1061        amount: Amount,
1062        from: ContractAddress,
1063        to: AccountAddress,
1064    ) -> Result<Amount, TransferError> {
1065        // Ensure the `to` account exists.
1066        if !self.chain.accounts.contains_key(&to.into()) {
1067            return Err(TransferError::ToMissing);
1068        }
1069
1070        // Make the transfer.
1071        let new_balance = self.change_contract_balance(from, AmountDelta::Negative(amount))?;
1072        self.change_account_balance(to, AmountDelta::Positive(amount))
1073            .expect("Cannot fail when adding");
1074
1075        Ok(new_balance)
1076    }
1077
1078    /// Make a transfer between contracts in the changeset.
1079    ///
1080    /// Returns the new balance of `to`.
1081    ///
1082    /// **Preconditions:**
1083    ///  - Assumes that `from` contract exists.
1084    fn transfer_from_contract_to_contract(
1085        &mut self,
1086        amount: Amount,
1087        from: ContractAddress,
1088        to: ContractAddress,
1089    ) -> Result<Amount, TransferError> {
1090        // Ensure the `to` contract exists.
1091        if !self.chain.contracts.contains_key(&to) {
1092            return Err(TransferError::ToMissing);
1093        }
1094
1095        // Make the transfer.
1096        self.change_contract_balance(from, AmountDelta::Negative(amount))?;
1097        let new_balance = self.change_contract_balance(to, AmountDelta::Positive(amount))?;
1098        Ok(new_balance)
1099    }
1100
1101    /// Make a transfer from an account to a contract in the changeset.
1102    ///
1103    /// Returns the new balance of `to`.
1104    ///
1105    /// **Preconditions:**
1106    ///  - Assumes that `from` account exists.
1107    fn transfer_from_account_to_contract(
1108        &mut self,
1109        amount: Amount,
1110        from: AccountAddress,
1111        to: ContractAddress,
1112    ) -> Result<Amount, TransferError> {
1113        // Ensure the `to` account exists.
1114        if !self.chain.contracts.contains_key(&to) {
1115            return Err(TransferError::ToMissing);
1116        }
1117
1118        // Make the transfer.
1119        self.change_account_balance(from, AmountDelta::Negative(amount))?;
1120        let new_balance = self.change_contract_balance(to, AmountDelta::Positive(amount))?;
1121        Ok(new_balance)
1122    }
1123
1124    /// Changes the contract balance in the topmost checkpoint on the changeset.
1125    ///
1126    /// If contract isn't already present in the changeset, it is added.
1127    ///
1128    /// Returns an error if the change in balance would go below `0`, which is a
1129    /// valid error, or if the amounts would overflow, which is an unrecoverable
1130    /// configuration error in the tests.
1131    /// Otherwise, it returns the new balance of the contract.
1132    ///
1133    /// The precondition is not part of the error type, as this is an internal
1134    /// helper function that is only called when the precondition is met.
1135    ///
1136    /// Returns the new balance.
1137    ///
1138    /// **Preconditions:**
1139    ///  - Contract must exist.
1140    fn change_contract_balance(
1141        &mut self,
1142        address: ContractAddress,
1143        delta: AmountDelta,
1144    ) -> Result<Amount, BalanceError> {
1145        match self.changeset.current_mut().contracts.entry(address) {
1146            btree_map::Entry::Vacant(vac) => {
1147                // get original balance
1148                let original_balance = self
1149                    .chain
1150                    .contracts
1151                    .get(&address)
1152                    .expect("Precondition violation: contract assumed to exist")
1153                    .self_balance;
1154                // Try to apply the balance or return an error if insufficient funds.
1155                let new_contract_balance = delta.apply_to_balance(original_balance)?;
1156                // Insert the changes into the changeset.
1157                vac.insert(ContractChanges {
1158                    self_balance_delta: delta,
1159                    ..ContractChanges::new(original_balance)
1160                });
1161                Ok(new_contract_balance)
1162            }
1163            btree_map::Entry::Occupied(mut occ) => {
1164                let contract_changes = occ.get_mut();
1165                let new_delta = contract_changes.self_balance_delta.add_delta(delta);
1166                // Try to apply the balance or return an error if insufficient funds.
1167                let new_contract_balance =
1168                    new_delta.apply_to_balance(contract_changes.self_balance_original)?;
1169                contract_changes.self_balance_delta = new_delta;
1170                Ok(new_contract_balance)
1171            }
1172        }
1173    }
1174
1175    /// Changes the account balance in the topmost checkpoint on the changeset.
1176    ///
1177    /// If account isn't already present in the changeset, it is added.
1178    ///
1179    /// Returns an error if a negative delta is provided which exceeds the
1180    /// available balance of the account. Otherwise, it returns the new
1181    /// available balance of the account.
1182    /// Otherwise, it returns the new balance of the account.
1183    ///
1184    /// The precondition is not part of the error type, as this is an internal
1185    /// helper function that is only called when the precondition is met.
1186    ///
1187    /// **Preconditions:**
1188    ///  - Account must exist.
1189    fn change_account_balance(
1190        &mut self,
1191        address: AccountAddress,
1192        delta: AmountDelta,
1193    ) -> Result<Amount, BalanceError> {
1194        match self.changeset.current_mut().accounts.entry(address.into()) {
1195            btree_map::Entry::Vacant(vac) => {
1196                // get original balance
1197                let mut original_balance = self
1198                    .chain
1199                    .accounts
1200                    .get(&address.into())
1201                    .expect("Precondition violation: account assumed to exist")
1202                    .balance
1203                    .available();
1204                if self.invoker == address {
1205                    // It has been checked that the invoker account has sufficient balance for
1206                    // paying.
1207                    original_balance -= self.reserved_amount;
1208                }
1209                // Try to apply the balance or return an error if insufficient funds.
1210                let new_account_balance = delta.apply_to_balance(original_balance)?;
1211                // Insert the changes into the changeset.
1212                vac.insert(AccountChanges {
1213                    original_balance,
1214                    balance_delta: delta,
1215                });
1216                Ok(new_account_balance)
1217            }
1218            btree_map::Entry::Occupied(mut occ) => {
1219                let account_changes = occ.get_mut();
1220                let new_delta = account_changes.balance_delta.add_delta(delta);
1221                // Try to apply the balance or return an error if insufficient funds.
1222                let new_account_balance =
1223                    new_delta.apply_to_balance(account_changes.original_balance)?;
1224                account_changes.balance_delta = new_delta;
1225                Ok(new_account_balance)
1226            }
1227        }
1228    }
1229
1230    /// Returns the contract balance from the topmost checkpoint on the
1231    /// changeset. Or, alternatively, from persistence.
1232    ///
1233    /// **Preconditions:**
1234    ///  - Contract must exist.
1235    fn contract_balance_unchecked(&self, address: ContractAddress) -> Amount {
1236        self.contract_balance(address).expect("Precondition violation: contract must exist")
1237    }
1238
1239    /// Looks up the contract balance from the topmost checkpoint on the
1240    /// changeset. Or, alternatively, from persistence.
1241    fn contract_balance(&self, address: ContractAddress) -> Option<Amount> {
1242        match self.changeset.current().contracts.get(&address) {
1243            Some(changes) => Some(changes.current_balance()),
1244            None => self.chain.contracts.get(&address).map(|c| c.self_balance),
1245        }
1246    }
1247
1248    /// Looks up the contract module reference from the topmost checkpoint on
1249    /// the changeset. Or, alternatively, from persistence.
1250    fn contract_module_reference(&self, address: ContractAddress) -> Option<ModuleReference> {
1251        // The module reference can change in the event of a contract upgrade, so we
1252        // need to look it up in the changeset first.
1253        self.changeset.current().contracts.get(&address).map_or_else(
1254            || self.chain.contracts.get(&address).map(|c| c.module_reference),
1255            |c| c.module,
1256        )
1257    }
1258
1259    /// Looks up the contract name from persistence. (Since the contract name
1260    /// cannot change, and new contracts cannot be deployed within contract
1261    /// invocations, this is sufficient to resolve the name of a contract.)
1262    fn contract_name(&self, address: ContractAddress) -> Option<ContractName> {
1263        // The contract name does not change as a result of upgrades (and contracts
1264        // cannot deploy new contracts), so we always resolve the contract name in the
1265        // immutable chain context.
1266        self.chain.contracts.get(&address).map(|c| c.contract_name.as_contract_name())
1267    }
1268
1269    /// Returns the contract module from the topmost checkpoint on
1270    /// the changeset. Or, alternatively, from persistence.
1271    ///
1272    /// **Preconditions:**
1273    ///  - Contract instance must exist (and therefore also the artifact).
1274    ///  - If the changeset contains a module reference, then it must refer a
1275    ///    deployed module.
1276    fn contract_module(&self, contract: &Contract) -> ContractModule {
1277        match self.changeset.current().contracts.get(&contract.address).and_then(|c| c.module) {
1278            // Contract has been upgrade, new module exists.
1279            Some(new_module) => self
1280                .chain
1281                .modules
1282                .get(&new_module)
1283                .expect("Precondition violation: module must exist.")
1284                .clone(),
1285            // Contract hasn't been upgraded. Use persisted module.
1286            None => self
1287                .chain
1288                .modules
1289                .get(&contract.module_reference)
1290                .expect("Precondition violation: module must exist.")
1291                .clone(),
1292        }
1293    }
1294
1295    /// Get the contract state, either from the changeset or by thawing it from
1296    /// persistence.
1297    ///
1298    /// **Preconditions:**
1299    ///  - Contract instance must exist.
1300    fn contract_state(&self, address: ContractAddress) -> trie::MutableState {
1301        match self.changeset.current().contracts.get(&address).and_then(|c| c.state.clone()) {
1302            // Contract state has been modified.
1303            Some(modified_state) => modified_state,
1304            // Contract state hasn't been modified. Thaw from persistence.
1305            None => self
1306                .chain
1307                .contracts
1308                .get(&address)
1309                .expect("Precondition violation: contract must exist")
1310                .state
1311                .thaw(),
1312        }
1313    }
1314
1315    /// Looks up the account balance for an account by first checking
1316    /// the changeset, then the persisted values.
1317    fn account_balance(&self, address: AccountAddress) -> Option<AccountBalance> {
1318        let mut account_balance = self.chain.accounts.get(&address.into()).map(|a| a.balance)?;
1319        match self.changeset.current().accounts.get(&address.into()).map(|a| a.current_balance()) {
1320            // Account exists in changeset.
1321            // Return the staked and locked balances from persistence, as they can't change during
1322            // entrypoint invocation.
1323            Some(total) => Some(AccountBalance {
1324                total,
1325                staked: account_balance.staked,
1326                locked: account_balance.locked,
1327            }),
1328            // Account doesn't exist in changeset.
1329            None => {
1330                if self.invoker == address {
1331                    account_balance.total -= self.reserved_amount;
1332                }
1333                Some(account_balance)
1334            }
1335        }
1336    }
1337
1338    /// Looks up the account keys.
1339    fn account_keys(&self, address: AccountAddress) -> Option<&AccountAccessStructure> {
1340        // The account keys cannot change during a smart contract transaction, so
1341        // there is no need to check in the changeset.
1342        self.chain.accounts.get(&address.into()).map(|a| &a.keys)
1343    }
1344
1345    /// Saves a mutable state for a contract in the changeset.
1346    ///
1347    /// If the contract already has an entry in the changeset, the old state
1348    /// will be replaced. Otherwise, the entry is created and the state is
1349    /// added.
1350    ///
1351    /// This method also increments the `self.next_contract_modification_index`.
1352    ///
1353    /// Returns the `modification_index` set for the contract.
1354    ///
1355    /// **Preconditions:**
1356    ///  - Contract must exist.
1357    fn save_state_changes(
1358        &mut self,
1359        address: ContractAddress,
1360        state: &mut trie::MutableState,
1361    ) -> u32 {
1362        let state = state.clone();
1363        let modification_index = self.next_contract_modification_index;
1364        match self.changeset.current_mut().contracts.entry(address) {
1365            btree_map::Entry::Vacant(vac) => {
1366                let original_balance = self
1367                    .chain
1368                    .contracts
1369                    .get(&address)
1370                    .expect("Precondition violation: contract must exist.")
1371                    .self_balance;
1372                vac.insert(ContractChanges {
1373                    state: Some(state),
1374                    modification_index,
1375                    ..ContractChanges::new(original_balance)
1376                });
1377            }
1378            btree_map::Entry::Occupied(mut occ) => {
1379                let changes = occ.get_mut();
1380                changes.state = Some(state);
1381                changes.modification_index = modification_index;
1382            }
1383        }
1384        self.next_contract_modification_index += 1;
1385        modification_index
1386    }
1387
1388    /// Saves a new module reference for the contract in the changeset.
1389    ///
1390    /// If the contract already has an entry in the changeset, the old module is
1391    /// replaced. Otherwise, the entry is created and the module is added.
1392    ///
1393    /// Returns the previous module, which is either the one from persistence,
1394    /// or the most recent one from the changeset.
1395    ///
1396    /// **Preconditions:**
1397    ///  - Contract must exist.
1398    ///  - Module must exist.
1399    fn save_module_upgrade(
1400        &mut self,
1401        address: ContractAddress,
1402        module_reference: ModuleReference,
1403    ) -> ModuleReference {
1404        match self.changeset.current_mut().contracts.entry(address) {
1405            btree_map::Entry::Vacant(vac) => {
1406                let contract = self
1407                    .chain
1408                    .contracts
1409                    .get(&address)
1410                    .expect("Precondition violation: contract must exist.");
1411                let old_module_ref = contract.module_reference;
1412                let original_balance = contract.self_balance;
1413                vac.insert(ContractChanges {
1414                    module: Some(module_reference),
1415                    ..ContractChanges::new(original_balance)
1416                });
1417                old_module_ref
1418            }
1419            btree_map::Entry::Occupied(mut occ) => {
1420                let changes = occ.get_mut();
1421                let old_module_ref = match changes.module {
1422                    Some(old_module) => old_module,
1423                    None => {
1424                        self.chain
1425                            .contracts
1426                            .get(&address)
1427                            .expect("Precondition violation: contract must exist.")
1428                            .module_reference
1429                    }
1430                };
1431                changes.module = Some(module_reference);
1432                old_module_ref
1433            }
1434        }
1435    }
1436
1437    /// Returns the modification index for a contract.
1438    ///
1439    /// It looks it up in the changeset, and if it isn't there, it will return
1440    /// `0`.
1441    fn modification_index(&self, address: ContractAddress) -> u32 {
1442        self.changeset.current().contracts.get(&address).map_or(0, |c| c.modification_index)
1443    }
1444
1445    /// Makes a new checkpoint.
1446    fn checkpoint(&mut self) { self.changeset.checkpoint(); }
1447
1448    /// Roll back to the previous checkpoint.
1449    fn rollback(&mut self) { self.changeset.rollback(); }
1450
1451    /// Update the `remaining_energy` field by converting the input to
1452    /// [`InterpreterEnergy`] and then [`Energy`].
1453    fn update_energy(&mut self, remaining_energy: InterpreterEnergy) {
1454        *self.remaining_energy = from_interpreter_energy(&remaining_energy);
1455    }
1456
1457    /// Run the interpreter with the provided function and the
1458    /// `self.remaining_energy`.
1459    ///
1460    /// This function ensures that the energy calculations is handled as in the
1461    /// node.
1462    fn run_interpreter<F, Err>(
1463        &mut self,
1464        f: F,
1465    ) -> Result<
1466        v1::ReceiveResult<artifact::CompiledFunction, DebugTracker>,
1467        (InsufficientEnergy, DebugTracker),
1468    >
1469    where
1470        F: FnOnce(
1471            InterpreterEnergy,
1472        )
1473            -> Result<v1::ReceiveResult<artifact::CompiledFunction, DebugTracker>, Err>,
1474        Err: Into<v1::ReceiveResult<artifact::CompiledFunction, DebugTracker>>, {
1475        let available_interpreter_energy = to_interpreter_energy(*self.remaining_energy);
1476        let res = match f(InterpreterEnergy::new(available_interpreter_energy)) {
1477            Ok(res) => res,
1478            Err(err) => {
1479                // An error occurred in the interpreter and it doesn't return the remaining
1480                // energy. We convert this to a trap and set the energy to 0 since this matches
1481                // the node behaviour.
1482                return Ok(err.into());
1483            }
1484        };
1485        let mut subtract_then_convert =
1486            |mut remaining_energy| -> Result<InterpreterEnergy, InsufficientEnergy> {
1487                // Using `saturating_sub` here should be ok since we should never be able to use
1488                // more energy than what is available.
1489                let used_energy = from_interpreter_energy(
1490                    &InterpreterEnergy::new(available_interpreter_energy)
1491                        .saturating_sub(&remaining_energy),
1492                );
1493                self.remaining_energy.tick_energy(used_energy)?;
1494                remaining_energy.energy = to_interpreter_energy(*self.remaining_energy);
1495                Ok(remaining_energy)
1496            };
1497        match res {
1498            v1::ReceiveResult::Success {
1499                logs,
1500                state_changed,
1501                return_value,
1502                remaining_energy,
1503                trace,
1504            } => {
1505                let Ok(remaining_energy) = subtract_then_convert(remaining_energy) else {
1506                    return Err((InsufficientEnergy, trace));
1507                };
1508                Ok(v1::ReceiveResult::Success {
1509                    logs,
1510                    state_changed,
1511                    return_value,
1512                    remaining_energy,
1513                    trace,
1514                })
1515            }
1516
1517            v1::ReceiveResult::Interrupt {
1518                remaining_energy,
1519                state_changed,
1520                logs,
1521                config,
1522                interrupt,
1523                trace,
1524            } => {
1525                let Ok(remaining_energy) = subtract_then_convert(remaining_energy) else {
1526                    return Err((InsufficientEnergy, trace));
1527                };
1528
1529                Ok(v1::ReceiveResult::Interrupt {
1530                    remaining_energy,
1531                    state_changed,
1532                    logs,
1533                    config,
1534                    interrupt,
1535                    trace,
1536                })
1537            }
1538            v1::ReceiveResult::Reject {
1539                reason,
1540                return_value,
1541                remaining_energy,
1542                trace,
1543            } => {
1544                let Ok(remaining_energy) = subtract_then_convert(remaining_energy) else {
1545                    return Err((InsufficientEnergy, trace));
1546                };
1547                Ok(v1::ReceiveResult::Reject {
1548                    reason,
1549                    return_value,
1550                    remaining_energy,
1551                    trace,
1552                })
1553            }
1554            v1::ReceiveResult::Trap {
1555                error,
1556                remaining_energy,
1557                trace,
1558            } => {
1559                let Ok(remaining_energy) = subtract_then_convert(remaining_energy) else {
1560                    return Err((InsufficientEnergy, trace));
1561                };
1562                Ok(v1::ReceiveResult::Trap {
1563                    error,
1564                    remaining_energy,
1565                    trace,
1566                })
1567            }
1568            // Convert this to an error so that we will short-circuit the processing.
1569            v1::ReceiveResult::OutOfEnergy {
1570                trace,
1571            } => Err((InsufficientEnergy, trace)),
1572        }
1573    }
1574
1575    /// The energy used so far in this transaction.
1576    fn energy_used(&self) -> Energy { self.energy_reserved - *self.remaining_energy }
1577
1578    /// Helper for that constructs and pushes a [`DebugTraceElement::Regular`]
1579    /// to the `trace_elements` list provided.
1580    fn push_regular_trace_element(
1581        &self,
1582        trace_elements: &mut Vec<DebugTraceElement>,
1583        trace_element: ContractTraceElement,
1584        entrypoint: OwnedEntrypointName,
1585        debug_trace: DebugTracker,
1586    ) {
1587        let energy_used = self.energy_used();
1588        let new = DebugTraceElement::Regular {
1589            entrypoint,
1590            trace_element,
1591            energy_used,
1592            debug_trace,
1593        };
1594        trace_elements.push(new);
1595    }
1596}
1597
1598/// A pair of the signatures, and the data.
1599type DeserializedSignatureAndData<'a> = (AccountSignatures, &'a [u8]);
1600
1601/// Deserialize the signatures and data from the slice.
1602/// Note that this does not ensure that the entire data is read, i.e., there can
1603/// be leftover data in the slice, which matches the behaviour in the node.
1604fn deserial_signature_and_data_from_contract(
1605    payload: &[u8],
1606) -> anyhow::Result<DeserializedSignatureAndData> {
1607    // Imported locally only since it is critical that we use the right Deserial
1608    // trait.
1609    use concordium_rust_sdk::base::contracts_common::Deserial;
1610    let mut source = base::contracts_common::Cursor::new(payload);
1611    let data_len = u32::deserial(&mut source)?;
1612    let data = &payload[source.offset..][..data_len as usize];
1613    source.offset += data_len as usize;
1614    let signatures = AccountSignatures::deserial(&mut source)?;
1615    Ok((signatures, data))
1616}
1617
1618impl ChangeSet {
1619    /// Creates a new changeset with one empty [`Changes`] element on the
1620    /// stack..
1621    pub(crate) fn new() -> Self {
1622        Self {
1623            stack: vec![Changes {
1624                contracts: BTreeMap::new(),
1625                accounts:  BTreeMap::new(),
1626            }],
1627        }
1628    }
1629
1630    /// Make a checkpoint by putting a clone of the top element onto the stack.
1631    fn checkpoint(&mut self) {
1632        let cloned_top_element = self.current().clone();
1633        self.stack.push(cloned_top_element);
1634    }
1635
1636    /// Perform a rollback by popping the top element of the stack.
1637    fn rollback(&mut self) {
1638        self.stack.pop().expect("Internal error: change set stack should never be empty.");
1639    }
1640
1641    /// Get an immutable reference the current (latest) checkpoint.
1642    fn current(&self) -> &Changes {
1643        self.stack.last().expect("Internal error: change set stack should never be empty.")
1644    }
1645
1646    /// Get a mutable reference to the current (latest) checkpoint.
1647    fn current_mut(&mut self) -> &mut Changes {
1648        self.stack.last_mut().expect("Internal error: change set stack should never be empty.")
1649    }
1650
1651    /// Try to persist all changes from the changeset.
1652    ///
1653    /// If the energy needed for storing extra state is larger than the
1654    /// `remaining_energy`, then:
1655    ///  - no changes will be persisted,
1656    ///  - an [`OutOfEnergy`] error is returned.
1657    ///
1658    /// Otherwise, it returns whether the state of the provided
1659    /// `invoked_contract` was changed.
1660    ///
1661    /// **Preconditions:**
1662    ///  - All contracts, modules, accounts referred must exist in persistence.
1663    ///  - All amount deltas must be valid (i.e. not cause underflows when added
1664    ///    to balance).
1665    pub(crate) fn persist(
1666        mut self,
1667        remaining_energy: &mut Energy,
1668        invoked_contract: ContractAddress,
1669        persisted_accounts: &mut BTreeMap<AccountAddressEq, Account>,
1670        persisted_contracts: &mut BTreeMap<ContractAddress, Contract>,
1671    ) -> Result<bool, InsufficientEnergy> {
1672        let current = self.current_mut();
1673        let mut invoked_contract_has_state_changes = false;
1674        // Persist contract changes and collect the total increase in states sizes.
1675        let mut collector = v1::trie::SizeCollector::default();
1676        let mut loader = v1::trie::Loader::new(&[][..]); // An empty loader is fine currently, as we do not use caching in this lib.
1677
1678        let mut frozen_states: BTreeMap<ContractAddress, trie::PersistentState> = BTreeMap::new();
1679
1680        // Create frozen versions of all the states, to compute the energy needed.
1681        for (addr, changes) in current.contracts.iter_mut() {
1682            if let Some(modified_state) = &mut changes.state {
1683                frozen_states.insert(*addr, modified_state.freeze(&mut loader, &mut collector));
1684            }
1685        }
1686
1687        // One energy per extra byte of state.
1688        let energy_for_state_increase = Energy::from(collector.collect());
1689        // Return an error if out of energy.
1690        remaining_energy.tick_energy(energy_for_state_increase)?;
1691
1692        // Then persist all the changes.
1693        for (addr, changes) in current.contracts.iter_mut() {
1694            let contract = persisted_contracts
1695                .get_mut(addr)
1696                .expect("Precondition violation: contract must exist");
1697            // Update balance.
1698            if !changes.self_balance_delta.is_zero() {
1699                contract.self_balance = changes
1700                    .self_balance_delta
1701                    .apply_to_balance(changes.self_balance_original)
1702                    .expect("Precondition violation: amount delta causes underflow");
1703            }
1704            // Update module reference.
1705            if let Some(new_module_ref) = changes.module {
1706                contract.module_reference = new_module_ref;
1707            }
1708            // Update state.
1709            if changes.state.is_some() {
1710                if *addr == invoked_contract {
1711                    invoked_contract_has_state_changes = true;
1712                }
1713                // Replace with the frozen state we created earlier.
1714                contract.state =
1715                    frozen_states.remove(addr).expect("Known to exist since we just added it.");
1716            }
1717        }
1718        // Persist account changes.
1719        for (addr, changes) in current.accounts.iter() {
1720            let account = persisted_accounts
1721                .get_mut(addr)
1722                .expect("Precondition violation: account must exist");
1723            // Update balance.
1724            if !changes.balance_delta.is_zero() {
1725                account.balance.total = changes
1726                    .balance_delta
1727                    .apply_to_balance(account.balance.total)
1728                    .expect("Precondition violation: amount delta causes underflow");
1729            }
1730        }
1731
1732        Ok(invoked_contract_has_state_changes)
1733    }
1734
1735    /// Traverses the last checkpoint in the changeset and collects the energy
1736    /// needed to be charged for additional state bytes.
1737    ///
1738    /// Returns an [`OutOfEnergy`] error if the energy needed for storing the
1739    /// extra state is larger than `remaining_energy`.
1740    ///
1741    /// Otherwise, it returns whether the state of the provided
1742    /// `invoked_contract` has changed.
1743    pub(crate) fn collect_energy_for_state(
1744        mut self,
1745        remaining_energy: &mut Energy,
1746        invoked_contract: ContractAddress,
1747    ) -> Result<bool, InsufficientEnergy> {
1748        let mut invoked_contract_has_state_changes = false;
1749        let mut loader = v1::trie::Loader::new(&[][..]); // An empty loader is fine currently, as we do not use caching in this lib.
1750        let mut collector = v1::trie::SizeCollector::default();
1751        for (addr, changes) in self.current_mut().contracts.iter_mut() {
1752            if let Some(modified_state) = &mut changes.state {
1753                if *addr == invoked_contract {
1754                    invoked_contract_has_state_changes = true;
1755                }
1756                modified_state.freeze(&mut loader, &mut collector);
1757            }
1758        }
1759
1760        // One energy per extra byte in the state.
1761        let energy_for_state_increase = Energy::from(collector.collect());
1762
1763        // Return an error if we run out of energy.
1764        remaining_energy.tick_energy(energy_for_state_increase)?;
1765
1766        Ok(invoked_contract_has_state_changes)
1767    }
1768}
1769
1770impl Default for ChangeSet {
1771    fn default() -> Self { Self::new() }
1772}
1773
1774impl AmountDelta {
1775    /// Create a new [`Self`], with the value `+0`.
1776    fn new() -> Self { Self::Positive(Amount::zero()) }
1777
1778    /// Subtract an [`Amount`] from [`Self`].
1779    fn subtract_amount(self, amount: Amount) -> Self {
1780        match self {
1781            Self::Positive(current) => {
1782                if current >= amount {
1783                    Self::Positive(current.subtract_micro_ccd(amount.micro_ccd))
1784                } else {
1785                    Self::Negative(amount.subtract_micro_ccd(current.micro_ccd))
1786                }
1787            }
1788            Self::Negative(current) => Self::Negative(current.add_micro_ccd(amount.micro_ccd)),
1789        }
1790    }
1791
1792    /// Add an [`Amount`] from [`Self`].
1793    fn add_amount(self, amount: Amount) -> Self {
1794        match self {
1795            Self::Positive(current) => Self::Positive(current.add_micro_ccd(amount.micro_ccd)),
1796            Self::Negative(current) => {
1797                if current >= amount {
1798                    Self::Negative(current.subtract_micro_ccd(amount.micro_ccd))
1799                } else {
1800                    Self::Positive(amount.subtract_micro_ccd(current.micro_ccd))
1801                }
1802            }
1803        }
1804    }
1805
1806    /// Add two [`Self`] to create a new one.
1807    fn add_delta(self, delta: AmountDelta) -> Self {
1808        match delta {
1809            AmountDelta::Positive(d) => self.add_amount(d),
1810            AmountDelta::Negative(d) => self.subtract_amount(d),
1811        }
1812    }
1813
1814    /// Whether the [`Self`] is zero (either `+0` or `-0`).
1815    fn is_zero(&self) -> bool {
1816        match self {
1817            AmountDelta::Positive(d) => d.micro_ccd == 0,
1818            AmountDelta::Negative(d) => d.micro_ccd == 0,
1819        }
1820    }
1821
1822    /// Returns a new balance with the `AmountDelta` applied, or a
1823    /// [`BalanceError`] error if the change would result in a negative
1824    /// balance or an overflow.
1825    fn apply_to_balance(&self, balance: Amount) -> Result<Amount, BalanceError> {
1826        match self {
1827            AmountDelta::Positive(d) => balance.checked_add(*d).ok_or(BalanceError::Overflow),
1828            AmountDelta::Negative(d) => balance.checked_sub(*d).ok_or(BalanceError::Insufficient),
1829        }
1830    }
1831}
1832
1833impl ContractChanges {
1834    /// Create a new `Self`. The original balance must be provided, all other
1835    /// fields take on default values.
1836    fn new(original_balance: Amount) -> Self {
1837        Self {
1838            modification_index:    0,
1839            self_balance_delta:    AmountDelta::new(),
1840            self_balance_original: original_balance,
1841            state:                 None,
1842            module:                None,
1843        }
1844    }
1845
1846    /// Get the current balance by adding the original balance and the balance
1847    /// delta.
1848    ///
1849    /// **Preconditions:**
1850    ///  - `balance_delta + original_balance` must be larger than `0`.
1851    fn current_balance(&self) -> Amount {
1852        self.self_balance_delta
1853            .apply_to_balance(self.self_balance_original)
1854            .expect("Precondition violation: invalid `balance_delta`.")
1855    }
1856}
1857
1858impl AccountChanges {
1859    /// Get the current balance by adding the original balance and the balance
1860    /// delta.
1861    ///
1862    /// **Preconditions:**
1863    ///  - `balance_delta + original_balance` must be larger than `0`.
1864    fn current_balance(&self) -> Amount {
1865        self.balance_delta
1866            .apply_to_balance(self.original_balance)
1867            .expect("Precondition violation: invalid `balance_delta`.")
1868    }
1869}
1870
1871impl From<(InsufficientEnergy, DebugTracker)> for TestConfigurationError {
1872    fn from((_, debug_trace): (InsufficientEnergy, DebugTracker)) -> Self {
1873        Self::OutOfEnergy {
1874            debug_trace,
1875        }
1876    }
1877}
1878
1879#[cfg(test)]
1880mod tests {
1881    mod amount_delta {
1882        use crate::{invocation::types::AmountDelta, Amount};
1883        #[test]
1884        fn test() {
1885            let mut x = AmountDelta::new();
1886            assert_eq!(x, AmountDelta::Positive(Amount::zero()));
1887
1888            let one = Amount::from_ccd(1);
1889            let two = Amount::from_ccd(2);
1890            let three = Amount::from_ccd(3);
1891            let five = Amount::from_ccd(5);
1892
1893            x = x.subtract_amount(one); // -1 CCD
1894            x = x.subtract_amount(one); // -2 CCD
1895            assert_eq!(x, AmountDelta::Negative(two));
1896            x = x.add_amount(five); // +3 CCD
1897            assert_eq!(x, AmountDelta::Positive(three));
1898            x = x.subtract_amount(five); // -2 CCD
1899            assert_eq!(x, AmountDelta::Negative(two));
1900            x = x.add_amount(two); // 0
1901
1902            x = x.add_amount(Amount::from_micro_ccd(1)); // 1 mCCD
1903            assert_eq!(x, AmountDelta::Positive(Amount::from_micro_ccd(1)));
1904            x = x.subtract_amount(Amount::from_micro_ccd(2)); // -1 mCCD
1905            assert_eq!(x, AmountDelta::Negative(Amount::from_micro_ccd(1)));
1906        }
1907    }
1908}