1use alloc::collections::{BTreeMap, BTreeSet};
2use alloc::vec::Vec;
3
4use anyhow::Context;
5
6const DEFAULT_FAUCET_DECIMALS: u8 = 10;
11
12const DEFAULT_VALIDATOR_COUNT: usize = 3;
16
17use itertools::Itertools;
21use miden_processor::crypto::random::RandomCoin;
22use miden_protocol::account::{
23 Account,
24 AccountBuilder,
25 AccountComponent,
26 AccountId,
27 AccountPatch,
28 AccountType,
29 AccountUpdateDetails,
30 StorageSlot,
31};
32use miden_protocol::asset::{Asset, AssetAmount, AssetId, FungibleAsset, TokenSymbol};
33use miden_protocol::block::account_tree::AccountTree;
34use miden_protocol::block::nullifier_tree::NullifierTree;
35use miden_protocol::block::{
36 BlockAccountUpdate,
37 BlockBody,
38 BlockHeader,
39 BlockNoteTree,
40 BlockNumber,
41 BlockSignatures,
42 Blockchain,
43 FeeParameters,
44 OutputNoteBatch,
45 ProvenBlock,
46 ValidatorConfig,
47};
48use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey;
49use miden_protocol::crypto::merkle::smt::Smt;
50use miden_protocol::errors::NoteError;
51use miden_protocol::note::{Note, NoteDetails, NoteScriptRoot, NoteType};
52use miden_protocol::protocol_config::ProtocolConfig;
53use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET;
54use miden_protocol::testing::random_secret_key::random_secret_key;
55use miden_protocol::transaction::{OrderedTransactionHeaders, RawOutputNote};
56use miden_protocol::{MAX_OUTPUT_NOTES_PER_BATCH, Word};
57use miden_standards::account::access::{AccessControl, Authority, Pausable, PausableManager};
58use miden_standards::account::auth::{AuthNetworkAccount, SponsorshipPolicy};
59use miden_standards::account::faucets::{FungibleFaucet, NonFungibleFaucet, TokenName};
60use miden_standards::account::fees::{BasicConstantFeePolicy, FeePolicyManager};
61use miden_standards::account::note_creator::NoteCreator;
62use miden_standards::account::policies::{
63 BurnPolicy,
64 MintPolicy,
65 TokenPolicyManager,
66 TransferPolicy,
67};
68use miden_standards::account::wallets::BasicWallet;
69use miden_standards::note::{BurnNote, MintNote, P2idNote, P2ideNote, SwapNote, TxFeeNote};
70use miden_standards::testing::account_component::MockAccountComponent;
71use rand::RngExt;
72
73use crate::mock_chain::chain::AccountAuthenticator;
74use crate::utils::{create_p2any_note, create_spawn_note};
75use crate::{AccountState, Auth, MockChain};
76
77#[derive(Debug, Clone)]
121pub struct MockChainBuilder {
122 accounts: BTreeMap<AccountId, Account>,
123 account_authenticators: BTreeMap<AccountId, AccountAuthenticator>,
124 notes: Vec<RawOutputNote>,
125 rng: RandomCoin,
126 validator_signing_keys: Option<Vec<SigningKey>>,
127 fee_faucet_id: AccountId,
129 verification_base_fee: u32,
130}
131
132impl MockChainBuilder {
133 pub fn new() -> Self {
146 let fee_faucet_id = ACCOUNT_ID_FEE_FAUCET.try_into().expect("account ID should be valid");
147
148 Self {
149 accounts: BTreeMap::new(),
150 account_authenticators: BTreeMap::new(),
151 notes: Vec::new(),
152 rng: RandomCoin::new(Default::default()),
153 validator_signing_keys: None,
154 fee_faucet_id,
155 verification_base_fee: 0,
156 }
157 }
158
159 pub fn with_accounts(accounts: impl IntoIterator<Item = Account>) -> anyhow::Result<Self> {
168 let mut builder = Self::new();
169
170 for account in accounts {
171 builder.add_account(account)?;
172 }
173
174 Ok(builder)
175 }
176
177 pub fn fee_faucet_id(mut self, fee_faucet_id: AccountId) -> Self {
185 self.fee_faucet_id = fee_faucet_id;
186 self
187 }
188
189 pub fn verification_base_fee(mut self, verification_base_fee: u32) -> Self {
193 self.verification_base_fee = verification_base_fee;
194 self
195 }
196
197 pub fn validator_signing_keys(mut self, keys: Vec<SigningKey>) -> Self {
206 self.validator_signing_keys = Some(keys);
207 self
208 }
209
210 pub fn build(self) -> anyhow::Result<MockChain> {
212 let block_account_updates: Vec<BlockAccountUpdate> = self
214 .accounts
215 .into_values()
216 .map(|account| {
217 let account_id = account.id();
218 let account_commitment = account.to_commitment();
219 let update_details = if account_id.is_private() {
220 AccountUpdateDetails::Private
221 } else {
222 let account_patch = AccountPatch::try_from(account)
223 .expect("chain builder should only store existing accounts without seeds");
224 AccountUpdateDetails::Public(account_patch)
225 };
226
227 BlockAccountUpdate::new(account_id, account_commitment, update_details)
228 })
229 .collect::<Result<_, _>>()?;
230
231 let account_tree = AccountTree::with_entries(
232 block_account_updates
233 .iter()
234 .map(|account| (account.account_id(), account.final_state_commitment())),
235 )
236 .context("failed to create genesis account tree")?;
237
238 let full_notes: Vec<Note> = self
240 .notes
241 .iter()
242 .filter_map(|note| match note {
243 RawOutputNote::Full(n) => Some(n.clone()),
244 _ => None,
245 })
246 .collect();
247
248 let proven_notes: Vec<_> = self
249 .notes
250 .into_iter()
251 .map(|note| note.into_output_note().expect("genesis note should be valid"))
252 .collect();
253 let note_chunks = proven_notes.into_iter().chunks(MAX_OUTPUT_NOTES_PER_BATCH);
254 let output_note_batches: Vec<OutputNoteBatch> = note_chunks
255 .into_iter()
256 .map(|batch_notes| batch_notes.into_iter().enumerate().collect::<Vec<_>>())
257 .collect();
258
259 let created_nullifiers = Vec::new();
260 let transactions = OrderedTransactionHeaders::new_unchecked(Vec::new());
261
262 let note_tree = BlockNoteTree::from_note_batches(&output_note_batches)
263 .context("failed to create block note tree")?;
264
265 let prev_block_commitment = Word::empty();
266 let block_num = BlockNumber::from(0u32);
267 let chain_commitment = Blockchain::new().commitment();
268 let account_root = account_tree.root();
269 let nullifier_root = NullifierTree::<Smt>::default().root();
270 let note_root = note_tree.root();
271 let tx_commitment = transactions.commitment();
272 let timestamp = MockChain::TIMESTAMP_START_SECS;
273 let fee_parameters = FeeParameters::new(self.verification_base_fee);
274 let protocol_config = ProtocolConfig::current(AssetId::new_fungible(self.fee_faucet_id))
275 .context("failed to build the genesis protocol config")?;
276 let validator_secret_keys = self
277 .validator_signing_keys
278 .unwrap_or_else(|| (0..DEFAULT_VALIDATOR_COUNT).map(|_| random_secret_key()).collect());
279 let quorum = u16::try_from(validator_secret_keys.len())
280 .context("genesis validator count exceeds u16::MAX")?;
281 let validator_config = ValidatorConfig::new(
282 validator_secret_keys.iter().map(|signer| signer.public_key()).collect(),
283 quorum,
284 )
285 .context("failed to build the genesis validator config")?;
286
287 let header = BlockHeader::new(
288 prev_block_commitment,
289 block_num,
290 chain_commitment,
291 account_root,
292 nullifier_root,
293 note_root,
294 tx_commitment,
295 validator_config.clone(),
296 fee_parameters,
297 protocol_config.to_commitment(),
298 None,
299 timestamp,
300 );
301
302 let body = BlockBody::new_unchecked(
303 block_account_updates,
304 output_note_batches,
305 created_nullifiers,
306 transactions,
307 );
308
309 let signatures = BlockSignatures::new(
312 validator_config
313 .keys()
314 .iter()
315 .map(|key| {
316 let signer = validator_secret_keys
317 .iter()
318 .find(|sk| &sk.public_key() == key)
319 .expect("a signer should exist for every validator key");
320 signer.sign(header.commitment())
321 })
322 .collect(),
323 )
324 .expect("signature count same as validator key count");
325 let block_proof = miden_protocol::testing::dummy_execution_proof();
326 let genesis_block = ProvenBlock::new_unchecked(header, body, signatures, block_proof);
327
328 MockChain::from_genesis_block(
329 genesis_block,
330 account_tree,
331 self.account_authenticators,
332 validator_secret_keys,
333 protocol_config,
334 full_notes,
335 )
336 }
337
338 pub fn create_new_wallet(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
347 let account_builder = AccountBuilder::new(self.rng.random())
348 .account_type(AccountType::Public)
349 .with_component(BasicWallet);
350
351 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
352 }
353
354 pub fn add_existing_wallet(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
357 self.add_existing_wallet_with_assets(auth_method, [])
358 }
359
360 pub fn add_existing_wallet_with_assets(
363 &mut self,
364 auth_method: Auth,
365 assets: impl IntoIterator<Item = Asset>,
366 ) -> anyhow::Result<Account> {
367 let account_builder = Account::builder(self.rng.random())
368 .account_type(AccountType::Public)
369 .with_component(BasicWallet)
370 .with_assets(assets);
371
372 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
373 }
374
375 pub fn add_existing_note_creator(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
381 let account_builder = Account::builder(self.rng.random())
382 .account_type(AccountType::Public)
383 .with_component(NoteCreator);
384
385 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
386 }
387
388 fn add_existing_network_fungible_faucet(
391 &mut self,
392 allowed_script_roots: BTreeSet<NoteScriptRoot>,
393 faucet: FungibleFaucet,
394 account_type: AccountType,
395 access_control: AccessControl,
396 token_policy_manager: TokenPolicyManager,
397 assets: Vec<Asset>,
398 ) -> anyhow::Result<Account> {
399 let mut basic_constant_fee_policy = BasicConstantFeePolicy::new();
404 for note_script in &allowed_script_roots {
405 basic_constant_fee_policy =
406 basic_constant_fee_policy.with_fee(*note_script, AssetAmount::ZERO);
407 }
408 for note_script in AuthNetworkAccount::default_allowed_note_scripts() {
411 basic_constant_fee_policy =
412 basic_constant_fee_policy.with_fee(note_script, AssetAmount::ZERO);
413 }
414
415 let fee_policy_manager = FeePolicyManager::builder()
416 .active_fee_policy(basic_constant_fee_policy.into())
417 .fee_faucet_id(self.fee_faucet_id)
418 .build();
419
420 let account_builder = AccountBuilder::new(self.rng.random())
421 .account_type(account_type)
422 .with_component(faucet)
423 .with_components(access_control)
424 .with_components(token_policy_manager)
425 .with_component(Pausable::unpaused())
426 .with_component(PausableManager)
427 .with_assets(assets);
428
429 let auth = Auth::NetworkAccount {
430 allowed_script_roots,
431 allowed_tx_script_roots: BTreeSet::new(),
432 fee_policy_manager,
433 sponsorship_policy: SponsorshipPolicy::default(),
434 };
435
436 self.add_account_from_builder(auth, account_builder, AccountState::Exists)
437 }
438
439 pub fn add_existing_basic_faucet(
449 &mut self,
450 auth_method: Auth,
451 token_symbol: &str,
452 max_supply: u64,
453 token_supply: Option<u64>,
454 ) -> anyhow::Result<Account> {
455 let token_supply = token_supply.unwrap_or(0);
456 let name = TokenName::new(token_symbol)?;
457 let symbol = TokenSymbol::new(token_symbol)
458 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
459 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
460 let token_supply = AssetAmount::new(token_supply).context("invalid token_supply")?;
461 let faucet = FungibleFaucet::builder()
462 .name(name)
463 .symbol(symbol)
464 .decimals(DEFAULT_FAUCET_DECIMALS)
465 .max_supply(max_supply)
466 .token_supply(token_supply)
467 .build()
468 .context("failed to build FungibleFaucet")?;
469
470 let token_policy_manager = TokenPolicyManager::builder()
471 .active_mint_policy(MintPolicy::allow_all())
472 .active_burn_policy(BurnPolicy::allow_all())
473 .build();
474
475 let account_builder = AccountBuilder::new(self.rng.random())
476 .account_type(AccountType::Public)
477 .with_component(faucet)
478 .with_component(Authority::AuthControlled)
479 .with_components(token_policy_manager)
480 .with_component(Pausable::unpaused())
481 .with_component(PausableManager);
482
483 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
484 }
485
486 pub fn add_existing_non_fungible_faucet(
492 &mut self,
493 auth_method: Auth,
494 token_symbol: &str,
495 ) -> anyhow::Result<Account> {
496 let name = TokenName::new(token_symbol)?;
497 let symbol = TokenSymbol::new(token_symbol)
498 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
499 let faucet = NonFungibleFaucet::builder().name(name).symbol(symbol).build();
500
501 let token_policy_manager = TokenPolicyManager::builder()
502 .active_mint_policy(MintPolicy::allow_all())
503 .active_burn_policy(BurnPolicy::allow_all())
504 .active_send_policy(TransferPolicy::allow_all())
505 .active_receive_policy(TransferPolicy::allow_all())
506 .build();
507
508 let account_builder = AccountBuilder::new(self.rng.random())
509 .account_type(AccountType::Public)
510 .with_component(faucet)
511 .with_component(Authority::AuthControlled)
512 .with_components(token_policy_manager)
513 .with_component(Pausable::unpaused());
514
515 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
516 }
517
518 pub fn add_existing_network_faucet(
529 &mut self,
530 token_symbol: &str,
531 max_supply: u64,
532 owner_account_id: AccountId,
533 token_supply: Option<u64>,
534 mint_policy: MintPolicy,
535 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
536 ) -> anyhow::Result<Account> {
537 self.add_existing_network_faucet_with_assets(
538 token_symbol,
539 max_supply,
540 owner_account_id,
541 token_supply,
542 mint_policy,
543 allowed_script_roots,
544 [],
545 )
546 }
547
548 #[allow(clippy::too_many_arguments)]
551 pub fn add_existing_network_faucet_with_assets(
552 &mut self,
553 token_symbol: &str,
554 max_supply: u64,
555 owner_account_id: AccountId,
556 token_supply: Option<u64>,
557 mint_policy: MintPolicy,
558 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
559 assets: impl IntoIterator<Item = Asset>,
560 ) -> anyhow::Result<Account> {
561 let token_supply = token_supply.unwrap_or(0);
562 let name = TokenName::new(token_symbol)?;
563 let symbol = TokenSymbol::new(token_symbol)
564 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
565 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
566 let token_supply = AssetAmount::new(token_supply).context("invalid token_supply")?;
567 let faucet = FungibleFaucet::builder()
568 .name(name)
569 .symbol(symbol)
570 .decimals(DEFAULT_FAUCET_DECIMALS)
571 .max_supply(max_supply)
572 .token_supply(token_supply)
573 .build()
574 .context("failed to build FungibleFaucet")?;
575
576 let token_policy_manager = TokenPolicyManager::builder()
577 .active_mint_policy(mint_policy)
578 .active_burn_policy(BurnPolicy::allow_all())
579 .active_send_policy(TransferPolicy::allow_all())
580 .active_receive_policy(TransferPolicy::allow_all())
581 .build();
582
583 let allowed_script_roots: BTreeSet<NoteScriptRoot> = allowed_script_roots
584 .into_iter()
585 .chain([MintNote::script_root(), BurnNote::script_root()])
586 .collect();
587
588 self.add_existing_network_fungible_faucet(
589 allowed_script_roots,
590 faucet,
591 AccountType::Public,
592 AccessControl::Ownable2Step { owner: owner_account_id },
593 token_policy_manager,
594 assets.into_iter().collect(),
595 )
596 }
597
598 pub fn add_existing_network_faucet_with_metadata(
605 &mut self,
606 owner_account_id: AccountId,
607 faucet: FungibleFaucet,
608 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
609 ) -> anyhow::Result<Account> {
610 let token_policy_manager = TokenPolicyManager::builder()
611 .active_mint_policy(MintPolicy::owner_only())
612 .active_burn_policy(BurnPolicy::allow_all())
613 .active_send_policy(TransferPolicy::allow_all())
614 .active_receive_policy(TransferPolicy::allow_all())
615 .build();
616
617 let allowed_script_roots: BTreeSet<NoteScriptRoot> = allowed_script_roots
618 .into_iter()
619 .chain([MintNote::script_root(), BurnNote::script_root()])
620 .collect();
621
622 self.add_existing_network_fungible_faucet(
623 allowed_script_roots,
624 faucet,
625 AccountType::Public,
626 AccessControl::Ownable2Step { owner: owner_account_id },
627 token_policy_manager,
628 Vec::new(),
629 )
630 }
631
632 pub fn create_new_faucet(
636 &mut self,
637 auth_method: Auth,
638 token_symbol: &str,
639 max_supply: u64,
640 ) -> anyhow::Result<Account> {
641 let name = TokenName::new(token_symbol)?;
642 let symbol = TokenSymbol::new(token_symbol)
643 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
644 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
645 let faucet = FungibleFaucet::builder()
646 .name(name)
647 .symbol(symbol)
648 .decimals(DEFAULT_FAUCET_DECIMALS)
649 .max_supply(max_supply)
650 .build()
651 .context("failed to build FungibleFaucet")?;
652
653 let token_policy_manager = TokenPolicyManager::builder()
654 .active_mint_policy(MintPolicy::allow_all())
655 .active_burn_policy(BurnPolicy::allow_all())
656 .build();
657
658 let account_builder = AccountBuilder::new(self.rng.random())
659 .account_type(AccountType::Public)
660 .with_component(faucet)
661 .with_component(Authority::AuthControlled)
662 .with_components(token_policy_manager)
663 .with_component(Pausable::unpaused())
664 .with_component(PausableManager);
665
666 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
667 }
668
669 pub fn create_new_mock_account(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
672 let account_builder = Account::builder(self.rng.random())
673 .account_type(AccountType::Public)
674 .with_component(MockAccountComponent::with_empty_slots());
675
676 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
677 }
678
679 pub fn add_existing_mock_account(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
682 self.add_existing_mock_account_with_storage_and_assets(auth_method, [], [])
683 }
684
685 pub fn add_existing_mock_account_with_storage(
688 &mut self,
689 auth_method: Auth,
690 slots: impl IntoIterator<Item = StorageSlot>,
691 ) -> anyhow::Result<Account> {
692 self.add_existing_mock_account_with_storage_and_assets(auth_method, slots, [])
693 }
694
695 pub fn add_existing_mock_account_with_assets(
698 &mut self,
699 auth_method: Auth,
700 assets: impl IntoIterator<Item = Asset>,
701 ) -> anyhow::Result<Account> {
702 self.add_existing_mock_account_with_storage_and_assets(auth_method, [], assets)
703 }
704
705 pub fn add_existing_mock_account_with_storage_and_assets(
708 &mut self,
709 auth_method: Auth,
710 slots: impl IntoIterator<Item = StorageSlot>,
711 assets: impl IntoIterator<Item = Asset>,
712 ) -> anyhow::Result<Account> {
713 let account_builder = Account::builder(self.rng.random())
714 .account_type(AccountType::Public)
715 .with_component(MockAccountComponent::with_slots(slots.into_iter().collect()))
716 .with_assets(assets);
717
718 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
719 }
720
721 pub fn add_account_from_builder(
732 &mut self,
733 auth_method: Auth,
734 mut account_builder: AccountBuilder,
735 account_state: AccountState,
736 ) -> anyhow::Result<Account> {
737 let (auth_components, authenticator) = auth_method.build_components();
738 account_builder = account_builder.with_components(auth_components);
739
740 let account = if let AccountState::New = account_state {
741 account_builder.build().context("failed to build account from builder")?
742 } else {
743 account_builder
744 .build_existing()
745 .context("failed to build account from builder")?
746 };
747
748 self.account_authenticators
749 .insert(account.id(), AccountAuthenticator::new(authenticator));
750
751 if let AccountState::Exists = account_state {
752 self.accounts.insert(account.id(), account.clone());
753 }
754
755 Ok(account)
756 }
757 pub fn add_existing_account_from_components(
758 &mut self,
759 auth: Auth,
760 components: impl IntoIterator<Item = AccountComponent>,
761 ) -> anyhow::Result<Account> {
762 let mut account_builder =
763 Account::builder(rand::rng().random()).account_type(AccountType::Public);
764
765 for component in components {
766 account_builder = account_builder.with_component(component);
767 }
768
769 self.add_account_from_builder(auth, account_builder, AccountState::Exists)
770 }
771
772 pub fn add_account(&mut self, account: Account) -> anyhow::Result<()> {
781 self.accounts.insert(account.id(), account);
782
783 Ok(())
786 }
787
788 pub fn add_output_note(&mut self, note: impl Into<RawOutputNote>) {
793 self.notes.push(note.into());
794 }
795
796 pub fn add_p2any_note(
801 &mut self,
802 sender_account_id: AccountId,
803 note_type: NoteType,
804 assets: impl IntoIterator<Item = Asset>,
805 ) -> anyhow::Result<Note> {
806 let note = create_p2any_note(sender_account_id, note_type, assets, &mut self.rng);
807 self.add_output_note(RawOutputNote::Full(note.clone()));
808
809 Ok(note)
810 }
811
812 pub fn add_p2id_note(
818 &mut self,
819 sender_account_id: AccountId,
820 target_account_id: AccountId,
821 asset: &[Asset],
822 note_type: NoteType,
823 ) -> Result<Note, NoteError> {
824 let note: Note = P2idNote::builder()
825 .sender(sender_account_id)
826 .target(target_account_id)
827 .assets(asset.iter().copied())
828 .note_type(note_type)
829 .generate_serial_number(&mut self.rng)
830 .build()?
831 .into();
832 self.add_output_note(RawOutputNote::Full(note.clone()));
833
834 Ok(note)
835 }
836
837 pub fn add_tx_fee_note(
843 &mut self,
844 sender_account_id: AccountId,
845 assets: &[Asset],
846 ) -> Result<Note, NoteError> {
847 let note: Note = TxFeeNote::builder()
848 .sender(sender_account_id)
849 .assets(assets.iter().copied())
850 .generate_serial_number(&mut self.rng)
851 .build()?
852 .into();
853 self.add_output_note(RawOutputNote::Full(note.clone()));
854
855 Ok(note)
856 }
857
858 pub fn add_p2ide_note(
866 &mut self,
867 sender_account_id: AccountId,
868 target_account_id: AccountId,
869 reclaimer: Option<AccountId>,
870 asset: &[Asset],
871 note_type: NoteType,
872 reclaim_height: Option<BlockNumber>,
873 timelock_height: Option<BlockNumber>,
874 ) -> Result<Note, NoteError> {
875 let note: Note = P2ideNote::builder()
876 .sender(sender_account_id)
877 .target(target_account_id)
878 .maybe_reclaimer(reclaimer)
879 .assets(asset.iter().copied())
880 .note_type(note_type)
881 .maybe_reclaim_height(reclaim_height)
882 .maybe_timelock_height(timelock_height)
883 .generate_serial_number(&mut self.rng)
884 .build()?
885 .into();
886
887 self.add_output_note(RawOutputNote::Full(note.clone()));
888
889 Ok(note)
890 }
891
892 pub fn add_swap_note(
894 &mut self,
895 sender: AccountId,
896 offered_asset: Asset,
897 requested_asset: Asset,
898 payback_note_type: NoteType,
899 ) -> anyhow::Result<(Note, NoteDetails)> {
900 let swap_note = SwapNote::builder()
901 .sender(sender)
902 .offered_asset(offered_asset)
903 .requested_asset(requested_asset)
904 .note_type(NoteType::Public)
905 .payback_note_type(payback_note_type)
906 .generate_serial_number(&mut self.rng)
907 .build()?;
908
909 let payback_note = swap_note.payback_note_details();
910 let note = Note::from(swap_note);
911
912 self.add_output_note(RawOutputNote::Full(note.clone()));
913
914 Ok((note, payback_note))
915 }
916
917 pub fn add_spawn_note<'note, I>(
928 &mut self,
929 output_notes: impl IntoIterator<Item = &'note Note, IntoIter = I>,
930 ) -> anyhow::Result<Note>
931 where
932 I: ExactSizeIterator<Item = &'note Note>,
933 {
934 let note = create_spawn_note(output_notes)?;
935 self.add_output_note(RawOutputNote::Full(note.clone()));
936
937 Ok(note)
938 }
939
940 pub fn add_p2id_note_with_fee(
947 &mut self,
948 target_account_id: AccountId,
949 amount: u64,
950 ) -> anyhow::Result<Note> {
951 let fee_asset = self.fee_asset(amount)?;
952 let note = self.add_p2id_note(
953 self.fee_faucet_id,
954 target_account_id,
955 &[Asset::from(fee_asset)],
956 NoteType::Public,
957 )?;
958
959 Ok(note)
960 }
961
962 pub fn rng_mut(&mut self) -> &mut RandomCoin {
969 &mut self.rng
970 }
971
972 fn fee_asset(&self, amount: u64) -> anyhow::Result<FungibleAsset> {
974 FungibleAsset::new(self.fee_faucet_id, amount).context("failed to create fee asset")
975 }
976}
977
978impl Default for MockChainBuilder {
979 fn default() -> Self {
980 Self::new()
981 }
982}