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}