1#![allow(clippy::cast_possible_truncation)]
7
8use std::{
9 collections::{BTreeMap, HashMap, VecDeque},
10 sync::{Arc, Mutex, MutexGuard},
11};
12
13use linera_base::{
14 abi::{ContractAbi, ServiceAbi},
15 data_types::{
16 Amount, ApplicationDescription, ApplicationPermissions, BlockHeight, Bytecode, Resources,
17 SendMessageRequest, Timestamp,
18 },
19 ensure, http,
20 identifiers::{
21 Account, AccountOwner, ApplicationId, BlobId, ChainId, DataBlobHash, ModuleId,
22 OwnerSpender, StreamName,
23 },
24 ownership::{AccountPermissionError, ChainOwnership, ManageChainError},
25 vm::VmRuntime,
26};
27use serde::Serialize;
28
29use crate::{Contract, KeyValueStore, ViewStorageContext};
30
31struct ExpectedPublishModuleCall {
32 contract: Bytecode,
33 service: Bytecode,
34 vm_runtime: VmRuntime,
35 formats: Option<Vec<u8>>,
36 module_id: ModuleId,
37}
38
39struct ExpectedCreateApplicationCall {
40 module_id: ModuleId,
41 parameters: Vec<u8>,
42 argument: Vec<u8>,
43 required_application_ids: Vec<ApplicationId>,
44 application_id: ApplicationId,
45}
46
47struct ExpectedCreateDataBlobCall {
48 bytes: Vec<u8>,
49 blob_id: BlobId,
50}
51
52pub struct MockContractRuntime<Application>
54where
55 Application: Contract,
56{
57 application_parameters: Option<Application::Parameters>,
58 application_id: Option<ApplicationId<Application::Abi>>,
59 application_creator_chain_id: Option<ChainId>,
60 application_descriptions: HashMap<ApplicationId, ApplicationDescription>,
61 chain_id: Option<ChainId>,
62 authenticated_owner: Option<Option<AccountOwner>>,
63 block_height: Option<BlockHeight>,
64 round: Option<u32>,
65 message_is_bouncing: Option<Option<bool>>,
66 message_origin_chain_id: Option<Option<ChainId>>,
67 message_origin_timestamp: Option<Option<Timestamp>>,
68 authenticated_caller_id: Option<Option<ApplicationId>>,
69 timestamp: Option<Timestamp>,
70 chain_balance: Option<Amount>,
71 owner_balances: Option<HashMap<AccountOwner, Amount>>,
72 allowances: HashMap<OwnerSpender, Amount>,
73 chain_ownership: Option<ChainOwnership>,
74 application_permissions: Option<ApplicationPermissions>,
75 can_manage_chain: Option<bool>,
76 call_application_handler: Option<CallApplicationHandler>,
77 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Application::Message>>>>,
78 outgoing_transfers: HashMap<Account, Amount>,
79 created_events: BTreeMap<StreamName, Vec<Vec<u8>>>,
80 events: BTreeMap<(ChainId, StreamName, u32), Vec<u8>>,
81 claim_requests: Vec<ClaimRequest>,
82 expected_service_queries: VecDeque<(ApplicationId, String, String)>,
83 expected_http_requests: VecDeque<(http::Request, http::Response)>,
84 expected_read_data_blob_requests: VecDeque<(DataBlobHash, Vec<u8>)>,
85 expected_assert_data_blob_exists_requests: VecDeque<(DataBlobHash, Option<()>)>,
86 expected_has_empty_storage_requests: VecDeque<(ApplicationId, bool)>,
87 expected_open_chain_calls: VecDeque<(
88 ChainOwnership,
89 ApplicationPermissions,
90 AccountOwner,
91 Amount,
92 ChainId,
93 )>,
94 expected_publish_module_calls: VecDeque<ExpectedPublishModuleCall>,
95 expected_create_application_calls: VecDeque<ExpectedCreateApplicationCall>,
96 expected_create_data_blob_calls: VecDeque<ExpectedCreateDataBlobCall>,
97 remaining_fuel: Option<u64>,
98 key_value_store: KeyValueStore,
99}
100
101impl<Application> Default for MockContractRuntime<Application>
102where
103 Application: Contract,
104{
105 fn default() -> Self {
106 MockContractRuntime::new()
107 }
108}
109
110impl<Application> MockContractRuntime<Application>
111where
112 Application: Contract,
113{
114 pub fn new() -> Self {
116 MockContractRuntime {
117 application_parameters: None,
118 application_id: None,
119 application_creator_chain_id: None,
120 application_descriptions: HashMap::new(),
121 chain_id: None,
122 authenticated_owner: None,
123 block_height: None,
124 round: None,
125 message_is_bouncing: None,
126 message_origin_chain_id: None,
127 message_origin_timestamp: None,
128 authenticated_caller_id: None,
129 timestamp: None,
130 chain_balance: None,
131 owner_balances: None,
132 allowances: HashMap::new(),
133 chain_ownership: None,
134 application_permissions: None,
135 can_manage_chain: None,
136 call_application_handler: None,
137 send_message_requests: Arc::default(),
138 outgoing_transfers: HashMap::new(),
139 created_events: BTreeMap::new(),
140 events: BTreeMap::new(),
141 claim_requests: Vec::new(),
142 expected_service_queries: VecDeque::new(),
143 expected_http_requests: VecDeque::new(),
144 expected_read_data_blob_requests: VecDeque::new(),
145 expected_assert_data_blob_exists_requests: VecDeque::new(),
146 expected_has_empty_storage_requests: VecDeque::new(),
147 expected_open_chain_calls: VecDeque::new(),
148 expected_publish_module_calls: VecDeque::new(),
149 expected_create_application_calls: VecDeque::new(),
150 expected_create_data_blob_calls: VecDeque::new(),
151 remaining_fuel: None,
152 key_value_store: KeyValueStore::mock().to_mut(),
153 }
154 }
155
156 pub fn key_value_store(&self) -> KeyValueStore {
158 self.key_value_store.clone()
159 }
160
161 pub fn root_view_storage_context(&self) -> ViewStorageContext {
163 ViewStorageContext::new_unchecked(self.key_value_store(), Vec::new(), ())
164 }
165
166 pub fn with_application_parameters(
168 mut self,
169 application_parameters: Application::Parameters,
170 ) -> Self {
171 self.application_parameters = Some(application_parameters);
172 self
173 }
174
175 pub fn set_application_parameters(
177 &mut self,
178 application_parameters: Application::Parameters,
179 ) -> &mut Self {
180 self.application_parameters = Some(application_parameters);
181 self
182 }
183
184 pub fn application_parameters(&mut self) -> Application::Parameters {
186 self.application_parameters.clone().expect(
187 "Application parameters have not been mocked, \
188 please call `MockContractRuntime::set_application_parameters` first",
189 )
190 }
191
192 pub fn with_application_id(mut self, application_id: ApplicationId<Application::Abi>) -> Self {
194 self.application_id = Some(application_id);
195 self
196 }
197
198 pub fn set_application_id(
200 &mut self,
201 application_id: ApplicationId<Application::Abi>,
202 ) -> &mut Self {
203 self.application_id = Some(application_id);
204 self
205 }
206
207 pub fn application_id(&mut self) -> ApplicationId<Application::Abi> {
209 self.application_id.expect(
210 "Application ID has not been mocked, \
211 please call `MockContractRuntime::set_application_id` first",
212 )
213 }
214
215 pub fn with_application_creator_chain_id(mut self, chain_id: ChainId) -> Self {
217 self.application_creator_chain_id = Some(chain_id);
218 self
219 }
220
221 pub fn set_application_creator_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
223 self.application_creator_chain_id = Some(chain_id);
224 self
225 }
226
227 pub fn application_creator_chain_id(&mut self) -> ChainId {
229 self.application_creator_chain_id.expect(
230 "Application creator chain ID has not been mocked, \
231 please call `MockContractRuntime::set_application_creator_chain_id` first",
232 )
233 }
234
235 pub fn with_application_description(
237 mut self,
238 application_id: ApplicationId,
239 description: ApplicationDescription,
240 ) -> Self {
241 self.application_descriptions
242 .insert(application_id, description);
243 self
244 }
245
246 pub fn set_application_description(
248 &mut self,
249 application_id: ApplicationId,
250 description: ApplicationDescription,
251 ) -> &mut Self {
252 self.application_descriptions
253 .insert(application_id, description);
254 self
255 }
256
257 pub fn read_application_description(
259 &mut self,
260 application_id: ApplicationId,
261 ) -> ApplicationDescription {
262 self.application_descriptions
263 .get(&application_id)
264 .cloned()
265 .unwrap_or_else(|| {
266 panic!(
267 "Application description for {application_id:?} has not been mocked, \
268 please call `MockContractRuntime::set_application_description` first"
269 )
270 })
271 }
272
273 pub fn with_chain_id(mut self, chain_id: ChainId) -> Self {
275 self.chain_id = Some(chain_id);
276 self
277 }
278
279 pub fn set_chain_id(&mut self, chain_id: ChainId) -> &mut Self {
281 self.chain_id = Some(chain_id);
282 self
283 }
284
285 pub fn chain_id(&mut self) -> ChainId {
287 self.chain_id.expect(
288 "Chain ID has not been mocked, \
289 please call `MockContractRuntime::set_chain_id` first",
290 )
291 }
292
293 pub fn with_authenticated_owner(
295 mut self,
296 authenticated_owner: impl Into<Option<AccountOwner>>,
297 ) -> Self {
298 self.authenticated_owner = Some(authenticated_owner.into());
299 self
300 }
301
302 pub fn set_authenticated_owner(
304 &mut self,
305 authenticated_owner: impl Into<Option<AccountOwner>>,
306 ) -> &mut Self {
307 self.authenticated_owner = Some(authenticated_owner.into());
308 self
309 }
310
311 pub fn authenticated_owner(&mut self) -> Option<AccountOwner> {
313 self.authenticated_owner.expect(
314 "Authenticated owner has not been mocked, \
315 please call `MockContractRuntime::set_authenticated_owner` first",
316 )
317 }
318
319 pub fn with_block_height(mut self, block_height: BlockHeight) -> Self {
321 self.block_height = Some(block_height);
322 self
323 }
324
325 pub fn set_block_height(&mut self, block_height: BlockHeight) -> &mut Self {
327 self.block_height = Some(block_height);
328 self
329 }
330
331 pub fn with_round(mut self, round: u32) -> Self {
333 self.round = Some(round);
334 self
335 }
336
337 pub fn set_round(&mut self, round: u32) -> &mut Self {
339 self.round = Some(round);
340 self
341 }
342
343 pub fn block_height(&mut self) -> BlockHeight {
345 self.block_height.expect(
346 "Block height has not been mocked, \
347 please call `MockContractRuntime::set_block_height` first",
348 )
349 }
350
351 pub fn with_message_is_bouncing(
353 mut self,
354 message_is_bouncing: impl Into<Option<bool>>,
355 ) -> Self {
356 self.message_is_bouncing = Some(message_is_bouncing.into());
357 self
358 }
359
360 pub fn set_message_is_bouncing(
362 &mut self,
363 message_is_bouncing: impl Into<Option<bool>>,
364 ) -> &mut Self {
365 self.message_is_bouncing = Some(message_is_bouncing.into());
366 self
367 }
368
369 pub fn message_is_bouncing(&mut self) -> Option<bool> {
372 self.message_is_bouncing.expect(
373 "`message_is_bouncing` flag has not been mocked, \
374 please call `MockContractRuntime::set_message_is_bouncing` first",
375 )
376 }
377
378 pub fn set_message_origin_chain_id(
380 &mut self,
381 message_origin_chain_id: impl Into<Option<ChainId>>,
382 ) -> &mut Self {
383 self.message_origin_chain_id = Some(message_origin_chain_id.into());
384 self
385 }
386
387 pub fn message_origin_chain_id(&mut self) -> Option<ChainId> {
390 self.message_origin_chain_id.expect(
391 "`message_origin_chain_id` has not been mocked, \
392 please call `MockContractRuntime::set_message_origin_chain_id` first",
393 )
394 }
395
396 pub fn set_message_origin_timestamp(
398 &mut self,
399 message_origin_timestamp: impl Into<Option<Timestamp>>,
400 ) -> &mut Self {
401 self.message_origin_timestamp = Some(message_origin_timestamp.into());
402 self
403 }
404
405 pub fn message_origin_timestamp(&mut self) -> Option<Timestamp> {
408 self.message_origin_timestamp.expect(
409 "`message_origin_timestamp` has not been mocked, \
410 please call `MockContractRuntime::set_message_origin_timestamp` first",
411 )
412 }
413
414 pub fn with_authenticated_caller_id(
416 mut self,
417 authenticated_caller_id: impl Into<Option<ApplicationId>>,
418 ) -> Self {
419 self.authenticated_caller_id = Some(authenticated_caller_id.into());
420 self
421 }
422
423 pub fn set_authenticated_caller_id(
425 &mut self,
426 authenticated_caller_id: impl Into<Option<ApplicationId>>,
427 ) -> &mut Self {
428 self.authenticated_caller_id = Some(authenticated_caller_id.into());
429 self
430 }
431
432 pub fn authenticated_caller_id(&mut self) -> Option<ApplicationId> {
435 self.authenticated_caller_id.expect(
436 "Authenticated caller ID has not been mocked, \
437 please call `MockContractRuntime::set_authenticated_caller_id` first",
438 )
439 }
440
441 pub fn check_account_permission(
443 &mut self,
444 owner: AccountOwner,
445 ) -> Result<(), AccountPermissionError> {
446 ensure!(
447 self.authenticated_owner() == Some(owner)
448 || self.authenticated_caller_id().map(AccountOwner::from) == Some(owner),
449 AccountPermissionError::NotPermitted(owner)
450 );
451 Ok(())
452 }
453
454 pub fn with_system_time(mut self, timestamp: Timestamp) -> Self {
456 self.timestamp = Some(timestamp);
457 self
458 }
459
460 pub fn set_system_time(&mut self, timestamp: Timestamp) -> &mut Self {
462 self.timestamp = Some(timestamp);
463 self
464 }
465
466 pub fn system_time(&mut self) -> Timestamp {
468 self.timestamp.expect(
469 "System time has not been mocked, \
470 please call `MockContractRuntime::set_system_time` first",
471 )
472 }
473
474 pub fn with_chain_balance(mut self, chain_balance: Amount) -> Self {
476 self.chain_balance = Some(chain_balance);
477 self
478 }
479
480 pub fn set_chain_balance(&mut self, chain_balance: Amount) -> &mut Self {
482 self.chain_balance = Some(chain_balance);
483 self
484 }
485
486 pub fn chain_balance(&mut self) -> Amount {
488 *self.chain_balance_mut()
489 }
490
491 fn chain_balance_mut(&mut self) -> &mut Amount {
493 self.chain_balance.as_mut().expect(
494 "Chain balance has not been mocked, \
495 please call `MockContractRuntime::set_chain_balance` first",
496 )
497 }
498
499 pub fn with_owner_balances(
501 mut self,
502 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
503 ) -> Self {
504 self.owner_balances = Some(owner_balances.into_iter().collect());
505 self
506 }
507
508 pub fn set_owner_balances(
510 &mut self,
511 owner_balances: impl IntoIterator<Item = (AccountOwner, Amount)>,
512 ) -> &mut Self {
513 self.owner_balances = Some(owner_balances.into_iter().collect());
514 self
515 }
516
517 pub fn with_owner_balance(mut self, owner: AccountOwner, balance: Amount) -> Self {
519 self.set_owner_balance(owner, balance);
520 self
521 }
522
523 pub fn set_owner_balance(&mut self, owner: AccountOwner, balance: Amount) -> &mut Self {
525 self.owner_balances
526 .get_or_insert_with(HashMap::new)
527 .insert(owner, balance);
528 self
529 }
530
531 pub fn owner_balance(&mut self, owner: AccountOwner) -> Amount {
533 *self.owner_balance_mut(owner)
534 }
535
536 pub fn with_allowances(
538 mut self,
539 allowances: impl IntoIterator<Item = (AccountOwner, AccountOwner, Amount)>,
540 ) -> Self {
541 self.set_allowances(allowances);
542 self
543 }
544
545 pub fn set_allowances(
547 &mut self,
548 allowances: impl IntoIterator<Item = (AccountOwner, AccountOwner, Amount)>,
549 ) -> &mut Self {
550 self.allowances = allowances
551 .into_iter()
552 .filter_map(|(owner, spender, amount)| {
553 if amount == Amount::ZERO {
554 None
555 } else {
556 Some((OwnerSpender::new(owner, spender), amount))
557 }
558 })
559 .collect();
560 self
561 }
562
563 pub fn with_allowance(
565 mut self,
566 owner: AccountOwner,
567 spender: AccountOwner,
568 allowance: Amount,
569 ) -> Self {
570 self.set_allowance(owner, spender, allowance);
571 self
572 }
573
574 pub fn set_allowance(
576 &mut self,
577 owner: AccountOwner,
578 spender: AccountOwner,
579 allowance: Amount,
580 ) -> &mut Self {
581 let owner_spender = OwnerSpender::new(owner, spender);
582 if allowance == Amount::ZERO {
583 self.allowances.remove(&owner_spender);
584 } else {
585 self.allowances.insert(owner_spender, allowance);
586 }
587 self
588 }
589
590 pub fn allowance(&self, owner: AccountOwner, spender: AccountOwner) -> Amount {
592 self.allowances
593 .get(&OwnerSpender::new(owner, spender))
594 .copied()
595 .unwrap_or(Amount::ZERO)
596 }
597
598 pub fn allowances(&self) -> Vec<(AccountOwner, AccountOwner, Amount)> {
600 self.allowances
601 .iter()
602 .map(|(owner_spender, amount)| (owner_spender.owner, owner_spender.spender, *amount))
603 .collect()
604 }
605
606 fn owner_balance_mut(&mut self, owner: AccountOwner) -> &mut Amount {
608 self.owner_balances
609 .as_mut()
610 .expect(
611 "Owner balances have not been mocked, \
612 please call `MockContractRuntime::set_owner_balances` first",
613 )
614 .get_mut(&owner)
615 .unwrap_or_else(|| {
616 panic!(
617 "Balance for owner {owner} was not mocked, \
618 please include a balance for them in the call to \
619 `MockContractRuntime::set_owner_balances`"
620 )
621 })
622 }
623
624 pub fn send_message(&mut self, destination: ChainId, message: Application::Message) {
626 self.prepare_message(message).send_to(destination)
627 }
628
629 pub fn prepare_message(
631 &mut self,
632 message: Application::Message,
633 ) -> MessageBuilder<Application::Message> {
634 MessageBuilder::new(message, self.send_message_requests.clone())
635 }
636
637 pub fn created_send_message_requests(
639 &self,
640 ) -> MutexGuard<'_, Vec<SendMessageRequest<Application::Message>>> {
641 self.send_message_requests
642 .try_lock()
643 .expect("Unit test should be single-threaded")
644 }
645
646 pub fn transfer(&mut self, source: AccountOwner, destination: Account, amount: Amount) {
649 self.debit(source, amount);
650
651 if Some(destination.chain_id) == self.chain_id {
652 self.credit(destination.owner, amount);
653 } else {
654 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
655 *destination_entry = destination_entry
656 .try_add(amount)
657 .expect("Outgoing transfer value overflow");
658 }
659 }
660
661 fn debit(&mut self, source: AccountOwner, amount: Amount) {
664 let source_balance = if source == AccountOwner::CHAIN {
665 self.chain_balance_mut()
666 } else {
667 self.owner_balance_mut(source)
668 };
669
670 *source_balance = source_balance
671 .try_sub(amount)
672 .expect("Insufficient funds in source account");
673 }
674
675 fn credit(&mut self, destination: AccountOwner, amount: Amount) {
678 let destination_balance = if destination == AccountOwner::CHAIN {
679 self.chain_balance_mut()
680 } else {
681 self.owner_balance_mut(destination)
682 };
683
684 *destination_balance = destination_balance
685 .try_add(amount)
686 .expect("Account balance overflow");
687 }
688
689 pub fn outgoing_transfers(&self) -> &HashMap<Account, Amount> {
691 &self.outgoing_transfers
692 }
693
694 pub fn claim(&mut self, source: Account, destination: Account, amount: Amount) {
696 if Some(source.chain_id) == self.chain_id {
697 self.debit(source.owner, amount);
698
699 if Some(destination.chain_id) == self.chain_id {
700 self.credit(destination.owner, amount);
701 }
702 }
703
704 self.claim_requests.push(ClaimRequest {
705 source,
706 amount,
707 destination,
708 });
709 }
710
711 pub fn claim_requests(&self) -> &[ClaimRequest] {
713 &self.claim_requests
714 }
715
716 pub fn approve(&mut self, owner: AccountOwner, spender: AccountOwner, amount: Amount) {
718 self.set_allowance(owner, spender, amount);
719 }
720
721 pub fn transfer_from(
724 &mut self,
725 owner: AccountOwner,
726 spender: AccountOwner,
727 destination: Account,
728 amount: Amount,
729 ) {
730 let owner_spender = OwnerSpender::new(owner, spender);
731 let remaining_allowance = self
732 .allowances
733 .get(&owner_spender)
734 .copied()
735 .unwrap_or(Amount::ZERO)
736 .try_sub(amount)
737 .expect("Insufficient allowance for transfer_from");
738
739 if remaining_allowance == Amount::ZERO {
740 self.allowances.remove(&owner_spender);
741 } else {
742 self.allowances.insert(owner_spender, remaining_allowance);
743 }
744
745 self.debit(owner, amount);
746
747 if Some(destination.chain_id) == self.chain_id {
748 self.credit(destination.owner, amount);
749 } else {
750 let destination_entry = self.outgoing_transfers.entry(destination).or_default();
751 *destination_entry = destination_entry
752 .try_add(amount)
753 .expect("Outgoing transfer value overflow");
754 }
755 }
756
757 pub fn with_chain_ownership(mut self, chain_ownership: ChainOwnership) -> Self {
759 self.chain_ownership = Some(chain_ownership);
760 self
761 }
762
763 pub fn set_chain_ownership(&mut self, chain_ownership: ChainOwnership) -> &mut Self {
765 self.chain_ownership = Some(chain_ownership);
766 self
767 }
768
769 pub fn chain_ownership(&mut self) -> ChainOwnership {
771 self.chain_ownership.clone().expect(
772 "Chain ownership has not been mocked, \
773 please call `MockContractRuntime::set_chain_ownership` first",
774 )
775 }
776
777 pub fn with_application_permissions(
779 mut self,
780 application_permissions: ApplicationPermissions,
781 ) -> Self {
782 self.application_permissions = Some(application_permissions);
783 self
784 }
785
786 pub fn set_application_permissions(
788 &mut self,
789 application_permissions: ApplicationPermissions,
790 ) -> &mut Self {
791 self.application_permissions = Some(application_permissions);
792 self
793 }
794
795 pub fn application_permissions(&mut self) -> ApplicationPermissions {
797 self.application_permissions.clone().expect(
798 "Application permissions have not been mocked, \
799 please call `MockContractRuntime::set_application_permissions` first",
800 )
801 }
802
803 pub fn with_can_manage_chain(mut self, can_manage_chain: bool) -> Self {
806 self.can_manage_chain = Some(can_manage_chain);
807 self
808 }
809
810 pub fn set_can_manage_chain(&mut self, can_manage_chain: bool) -> &mut Self {
813 self.can_manage_chain = Some(can_manage_chain);
814 self
815 }
816
817 pub fn close_chain(&mut self) -> Result<(), ManageChainError> {
820 let authorized = self.can_manage_chain.expect(
821 "Authorization to manage the chain has not been mocked, \
822 please call `MockContractRuntime::set_can_manage_chain` first",
823 );
824
825 if authorized {
826 Ok(())
827 } else {
828 Err(ManageChainError::NotPermitted)
829 }
830 }
831
832 pub fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ManageChainError> {
835 let authorized = self.can_manage_chain.expect(
836 "Authorization to manage the chain has not been mocked, \
837 please call `MockContractRuntime::set_can_manage_chain` first",
838 );
839
840 if authorized {
841 self.chain_ownership = Some(ownership);
842 Ok(())
843 } else {
844 Err(ManageChainError::NotPermitted)
845 }
846 }
847
848 pub fn change_application_permissions(
851 &mut self,
852 application_permissions: ApplicationPermissions,
853 ) -> Result<(), ManageChainError> {
854 let authorized = self.can_manage_chain.expect(
855 "Authorization to manage the chain has not been mocked, \
856 please call `MockContractRuntime::set_can_manage_chain` first",
857 );
858
859 if authorized {
860 let application_id = self
861 .application_id
862 .expect("The application doesn't have an ID!")
863 .forget_abi();
864 self.can_manage_chain = Some(application_permissions.can_manage_chain(&application_id));
865 Ok(())
866 } else {
867 Err(ManageChainError::NotPermitted)
868 }
869 }
870
871 pub fn add_expected_open_chain_call(
873 &mut self,
874 ownership: ChainOwnership,
875 application_permissions: ApplicationPermissions,
876 account: AccountOwner,
877 balance: Amount,
878 chain_id: ChainId,
879 ) {
880 self.expected_open_chain_calls.push_back((
881 ownership,
882 application_permissions,
883 account,
884 balance,
885 chain_id,
886 ));
887 }
888
889 pub fn open_chain(
893 &mut self,
894 ownership: ChainOwnership,
895 application_permissions: ApplicationPermissions,
896 account: AccountOwner,
897 balance: Amount,
898 ) -> ChainId {
899 let (
900 expected_ownership,
901 expected_permissions,
902 expected_account,
903 expected_balance,
904 chain_id,
905 ) = self
906 .expected_open_chain_calls
907 .pop_front()
908 .expect("Unexpected open_chain call");
909 assert_eq!(&ownership, &expected_ownership);
910 assert_eq!(&application_permissions, &expected_permissions);
911 assert_eq!(account, expected_account);
912 assert_eq!(balance, expected_balance);
913 chain_id
914 }
915
916 pub fn add_expected_publish_module_call(
918 &mut self,
919 contract: Bytecode,
920 service: Bytecode,
921 vm_runtime: VmRuntime,
922 formats: Option<Vec<u8>>,
923 module_id: ModuleId,
924 ) {
925 self.expected_publish_module_calls
926 .push_back(ExpectedPublishModuleCall {
927 contract,
928 service,
929 vm_runtime,
930 formats,
931 module_id,
932 });
933 }
934
935 pub fn add_expected_create_application_call<Parameters, InstantiationArgument>(
937 &mut self,
938 module_id: ModuleId,
939 parameters: Parameters,
940 argument: InstantiationArgument,
941 required_application_ids: Vec<ApplicationId>,
942 application_id: ApplicationId,
943 ) where
944 Parameters: Serialize,
945 InstantiationArgument: Serialize,
946 {
947 let parameters = serde_json::to_vec(¶meters)
948 .expect("Failed to serialize `Parameters` type for a cross-application call");
949 let argument = serde_json::to_vec(&argument).expect(
950 "Failed to serialize `InstantiationArgument` type for a cross-application call",
951 );
952 self.expected_create_application_calls
953 .push_back(ExpectedCreateApplicationCall {
954 module_id,
955 parameters,
956 argument,
957 required_application_ids,
958 application_id,
959 });
960 }
961
962 pub fn add_expected_create_data_blob_call(&mut self, bytes: Vec<u8>, blob_id: BlobId) {
964 self.expected_create_data_blob_calls
965 .push_back(ExpectedCreateDataBlobCall { bytes, blob_id });
966 }
967
968 pub fn publish_module(
970 &mut self,
971 contract: Bytecode,
972 service: Bytecode,
973 vm_runtime: VmRuntime,
974 formats: Option<Vec<u8>>,
975 ) -> ModuleId {
976 let ExpectedPublishModuleCall {
977 contract: expected_contract,
978 service: expected_service,
979 vm_runtime: expected_vm_runtime,
980 formats: expected_formats,
981 module_id,
982 } = self
983 .expected_publish_module_calls
984 .pop_front()
985 .expect("Unexpected publish_module call");
986 assert_eq!(&contract, &expected_contract);
987 assert_eq!(&service, &expected_service);
988 assert_eq!(vm_runtime, expected_vm_runtime);
989 assert_eq!(formats, expected_formats);
990 module_id
991 }
992
993 pub fn create_application<Abi, Parameters, InstantiationArgument>(
995 &mut self,
996 module_id: ModuleId,
997 parameters: &Parameters,
998 argument: &InstantiationArgument,
999 required_application_ids: Vec<ApplicationId>,
1000 ) -> ApplicationId<Abi>
1001 where
1002 Abi: ContractAbi,
1003 Parameters: Serialize,
1004 InstantiationArgument: Serialize,
1005 {
1006 let ExpectedCreateApplicationCall {
1007 module_id: expected_module_id,
1008 parameters: expected_parameters,
1009 argument: expected_argument,
1010 required_application_ids: expected_required_app_ids,
1011 application_id,
1012 } = self
1013 .expected_create_application_calls
1014 .pop_front()
1015 .expect("Unexpected create_application call");
1016 let parameters = serde_json::to_vec(parameters)
1017 .expect("Failed to serialize `Parameters` type for a cross-application call");
1018 let argument = serde_json::to_vec(argument).expect(
1019 "Failed to serialize `InstantiationArgument` type for a cross-application call",
1020 );
1021 assert_eq!(module_id, expected_module_id);
1022 assert_eq!(parameters, expected_parameters);
1023 assert_eq!(argument, expected_argument);
1024 assert_eq!(
1025 required_application_ids.as_slice(),
1026 expected_required_app_ids.as_slice()
1027 );
1028 application_id.with_abi::<Abi>()
1029 }
1030
1031 pub fn create_data_blob(&mut self, bytes: Vec<u8>) -> DataBlobHash {
1033 let ExpectedCreateDataBlobCall {
1034 bytes: expected_bytes,
1035 blob_id,
1036 } = self
1037 .expected_create_data_blob_calls
1038 .pop_front()
1039 .expect("Unexpected create_data_blob call");
1040 assert_eq!(bytes, expected_bytes);
1041 DataBlobHash(blob_id.hash)
1042 }
1043
1044 pub fn with_call_application_handler(
1046 mut self,
1047 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
1048 ) -> Self {
1049 self.call_application_handler = Some(Box::new(handler));
1050 self
1051 }
1052
1053 pub fn set_call_application_handler(
1055 &mut self,
1056 handler: impl FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8> + 'static,
1057 ) -> &mut Self {
1058 self.call_application_handler = Some(Box::new(handler));
1059 self
1060 }
1061
1062 pub fn call_application<A: ContractAbi + Send>(
1064 &mut self,
1065 authenticated: bool,
1066 application: ApplicationId<A>,
1067 call: &A::Operation,
1068 ) -> A::Response {
1069 let call_bytes = <A as ContractAbi>::serialize_operation(call)
1070 .expect("Failed to serialize `Operation` in test runtime cross-application call");
1071
1072 let handler = self.call_application_handler.as_mut().expect(
1073 "Handler for `call_application` has not been mocked, \
1074 please call `MockContractRuntime::set_call_application_handler` first",
1075 );
1076 let response_bytes = handler(authenticated, application.forget_abi(), call_bytes);
1077
1078 A::deserialize_response(response_bytes)
1079 .expect("Failed to deserialize `Response` in test runtime cross-application call")
1080 }
1081
1082 pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
1084 let value = bcs::to_bytes(value).expect("Failed to serialize event value");
1085 let entry = self.created_events.entry(name).or_default();
1086 entry.push(value);
1087 entry.len() as u32 - 1
1088 }
1089
1090 pub fn add_event(&mut self, chain_id: ChainId, name: StreamName, index: u32, value: &[u8]) {
1092 self.events.insert((chain_id, name, index), value.to_vec());
1093 }
1094
1095 pub fn read_event(
1099 &mut self,
1100 chain_id: ChainId,
1101 name: StreamName,
1102 index: u32,
1103 ) -> Application::EventValue {
1104 let value = self
1105 .events
1106 .get(&(chain_id, name, index))
1107 .expect("Event not found");
1108 bcs::from_bytes(value).expect("Failed to deserialize event value")
1109 }
1110
1111 pub fn subscribe_to_events(
1113 &mut self,
1114 _chain_id: ChainId,
1115 _application_id: ApplicationId,
1116 _name: StreamName,
1117 ) {
1118 }
1120
1121 pub fn unsubscribe_from_events(
1123 &mut self,
1124 _chain_id: ChainId,
1125 _application_id: ApplicationId,
1126 _name: StreamName,
1127 ) {
1128 }
1130
1131 pub fn add_expected_service_query<A: ServiceAbi + Send>(
1133 &mut self,
1134 application_id: ApplicationId<A>,
1135 query: A::Query,
1136 response: A::QueryResponse,
1137 ) {
1138 let query = serde_json::to_string(&query).expect("Failed to serialize query");
1139 let response = serde_json::to_string(&response).expect("Failed to serialize response");
1140 self.expected_service_queries
1141 .push_back((application_id.forget_abi(), query, response));
1142 }
1143
1144 pub fn add_expected_http_request(&mut self, request: http::Request, response: http::Response) {
1146 self.expected_http_requests.push_back((request, response));
1147 }
1148
1149 pub fn add_expected_read_data_blob_requests(&mut self, hash: DataBlobHash, response: Vec<u8>) {
1151 self.expected_read_data_blob_requests
1152 .push_back((hash, response));
1153 }
1154
1155 pub fn add_expected_assert_data_blob_exists_requests(
1157 &mut self,
1158 hash: DataBlobHash,
1159 response: Option<()>,
1160 ) {
1161 self.expected_assert_data_blob_exists_requests
1162 .push_back((hash, response));
1163 }
1164
1165 pub fn add_expected_has_empty_storage_requests(
1167 &mut self,
1168 application: ApplicationId,
1169 response: bool,
1170 ) {
1171 self.expected_has_empty_storage_requests
1172 .push_back((application, response));
1173 }
1174
1175 pub fn query_service<A: ServiceAbi + Send>(
1183 &mut self,
1184 application_id: ApplicationId<A>,
1185 query: A::Query,
1186 ) -> A::QueryResponse {
1187 let maybe_query = self.expected_service_queries.pop_front();
1188 let (expected_id, expected_query, response) =
1189 maybe_query.expect("Unexpected service query");
1190 assert_eq!(application_id.forget_abi(), expected_id);
1191 let query = serde_json::to_string(&query).expect("Failed to serialize query");
1192 assert_eq!(query, expected_query);
1193 serde_json::from_str(&response).expect("Failed to deserialize response")
1194 }
1195
1196 pub fn http_request(&mut self, request: http::Request) -> http::Response {
1204 let maybe_request = self.expected_http_requests.pop_front();
1205 let (expected_request, response) = maybe_request.expect("Unexpected HTTP request");
1206 assert_eq!(&request, &expected_request);
1207 response
1208 }
1209
1210 pub fn assert_before(&mut self, timestamp: Timestamp) {
1216 assert!(self.timestamp.is_some_and(|t| t < timestamp))
1217 }
1218
1219 pub fn read_data_blob(&mut self, hash: DataBlobHash) -> Vec<u8> {
1221 let maybe_request = self.expected_read_data_blob_requests.pop_front();
1222 let (expected_hash, response) = maybe_request.expect("Unexpected read_data_blob request");
1223 assert_eq!(hash, expected_hash);
1224 response
1225 }
1226
1227 pub fn assert_data_blob_exists(&mut self, hash: DataBlobHash) {
1229 let maybe_request = self.expected_assert_data_blob_exists_requests.pop_front();
1230 let (expected_blob_hash, response) =
1231 maybe_request.expect("Unexpected assert_data_blob_exists request");
1232 assert_eq!(hash, expected_blob_hash);
1233 response.expect("Blob does not exist!");
1234 }
1235
1236 pub fn has_empty_storage(&mut self, application: ApplicationId) -> bool {
1238 let maybe_request = self.expected_has_empty_storage_requests.pop_front();
1239 let (expected_application_id, response) =
1240 maybe_request.expect("Unexpected has_empty_storage request");
1241 assert_eq!(application, expected_application_id);
1242 response
1243 }
1244
1245 pub fn validation_round(&mut self) -> Option<u32> {
1247 self.round
1248 }
1249
1250 pub fn with_remaining_fuel(mut self, remaining_fuel: u64) -> Self {
1252 self.remaining_fuel = Some(remaining_fuel);
1253 self
1254 }
1255
1256 pub fn set_remaining_fuel(&mut self, remaining_fuel: u64) -> &mut Self {
1258 self.remaining_fuel = Some(remaining_fuel);
1259 self
1260 }
1261
1262 pub fn remaining_fuel(&mut self) -> u64 {
1264 self.remaining_fuel.unwrap_or(u64::MAX)
1265 }
1266}
1267
1268pub type CallApplicationHandler = Box<dyn FnMut(bool, ApplicationId, Vec<u8>) -> Vec<u8>>;
1270
1271#[must_use]
1274pub struct MessageBuilder<Message>
1275where
1276 Message: Serialize,
1277{
1278 authenticated: bool,
1279 is_tracked: bool,
1280 grant: Resources,
1281 message: Message,
1282 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1283}
1284
1285impl<Message> MessageBuilder<Message>
1286where
1287 Message: Serialize,
1288{
1289 pub(crate) fn new(
1291 message: Message,
1292 send_message_requests: Arc<Mutex<Vec<SendMessageRequest<Message>>>>,
1293 ) -> Self {
1294 MessageBuilder {
1295 authenticated: false,
1296 is_tracked: false,
1297 grant: Resources::default(),
1298 message,
1299 send_message_requests,
1300 }
1301 }
1302
1303 pub fn with_tracking(mut self) -> Self {
1306 self.is_tracked = true;
1307 self
1308 }
1309
1310 pub fn with_authentication(mut self) -> Self {
1312 self.authenticated = true;
1313 self
1314 }
1315
1316 pub fn with_grant(mut self, grant: Resources) -> Self {
1318 self.grant = grant;
1319 self
1320 }
1321
1322 pub fn send_to(self, destination: ChainId) {
1324 let request = SendMessageRequest {
1325 destination,
1326 authenticated: self.authenticated,
1327 is_tracked: self.is_tracked,
1328 grant: self.grant,
1329 message: self.message,
1330 };
1331
1332 self.send_message_requests
1333 .try_lock()
1334 .expect("Unit test should be single-threaded")
1335 .push(request);
1336 }
1337}
1338
1339#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1341pub struct ClaimRequest {
1342 source: Account,
1343 destination: Account,
1344 amount: Amount,
1345}