1use std::boxed::Box;
2use std::collections::BTreeMap;
3use std::env::temp_dir;
4use std::fs::OpenOptions;
5use std::io::Write;
6use std::ops::{Deref, DerefMut};
7use std::path::PathBuf;
8use std::string::{String, ToString};
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11use std::vec::Vec;
12
13use anyhow::{Context, Result};
14use miden_protocol::account::auth::AuthSecretKey;
15use miden_protocol::account::{Account, AccountId};
16use miden_protocol::asset::{AssetAmount, FungibleAsset, TokenSymbol};
17use miden_protocol::note::NoteType;
18use miden_protocol::testing::account_id::ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE;
19use miden_protocol::transaction::TransactionId;
20use miden_standards::account::auth::{Approver, AuthSingleSig};
21use miden_standards::account::faucets::TokenName;
22use rand::Rng;
23use tracing::{debug, info};
24use uuid::Uuid;
25
26use crate::account::component::{
27 BasicWallet,
28 BurnPolicy,
29 FungibleFaucet,
30 MintPolicy,
31 TokenPolicyManager,
32};
33use crate::account::{AccountBuilder, AccountBuilderSchemaCommitmentExt, AccountType};
34use crate::auth::{AuthSchemeId, ECDSA_K256_KECCAK_SCHEME_ID};
35pub use crate::keystore::{FilesystemKeyStore, Keystore};
36use crate::note::{Note, NoteConsumability, P2idNote};
37use crate::rpc::RpcError;
38use crate::store::{InputNoteRecord, NoteFilter, TransactionFilter};
39use crate::sync::SyncSummary;
40use crate::test_utils::fee::FeeFunder;
41use crate::transaction::{
42 NoteArgs,
43 TransactionRequest,
44 TransactionRequestBuilder,
45 TransactionRequestError,
46 TransactionResult,
47 TransactionStatus,
48};
49use crate::{Client, ClientError};
50
51pub struct TestClient {
59 client: Client<FilesystemKeyStore>,
60 fee_funder: Option<Arc<dyn FeeFunder>>,
61 pending_funding: BTreeMap<AccountId, Note>,
66}
67
68impl TestClient {
69 pub fn new(client: Client<FilesystemKeyStore>) -> Self {
71 Self {
72 client,
73 fee_funder: None,
74 pending_funding: BTreeMap::new(),
75 }
76 }
77
78 pub fn keystore(&self) -> &FilesystemKeyStore {
80 self.client
81 .authenticator()
82 .expect("test clients are always built with a keystore authenticator")
83 .as_ref()
84 }
85
86 pub(crate) fn stash_funding(&mut self, funded: impl IntoIterator<Item = (AccountId, Note)>) {
88 self.pending_funding.extend(funded);
89 }
90
91 pub fn take_funding(&mut self, account_id: AccountId) -> Option<Note> {
97 self.pending_funding.remove(&account_id)
98 }
99
100 pub async fn submit_new_transaction(
109 &mut self,
110 account_id: AccountId,
111 transaction_request: TransactionRequest,
112 ) -> Result<TransactionId, ClientError> {
113 self.sync_state().await?;
114
115 let transaction_request = self.fund_request(account_id, transaction_request);
116
117 Box::pin(self.client.submit_new_transaction(account_id, transaction_request)).await
118 }
119
120 pub async fn execute_transaction(
126 &mut self,
127 account_id: AccountId,
128 transaction_request: TransactionRequest,
129 ) -> Result<TransactionResult, ClientError> {
130 self.sync_state().await?;
131
132 let transaction_request = self.fund_request(account_id, transaction_request);
133
134 Box::pin(self.client.execute_transaction(account_id, transaction_request)).await
135 }
136
137 #[must_use]
142 pub fn fund_request(
143 &mut self,
144 account_id: AccountId,
145 mut transaction_request: TransactionRequest,
146 ) -> TransactionRequest {
147 if let Some(note) = self.take_funding(account_id) {
148 transaction_request.add_unauthenticated_input_note(note);
149 }
150
151 transaction_request
152 }
153
154 #[must_use]
156 pub fn with_fee_funder(mut self, fee_funder: Option<Arc<dyn FeeFunder>>) -> Self {
157 self.fee_funder = fee_funder;
158 self
159 }
160
161 pub fn fee_funder(&self) -> Option<&Arc<dyn FeeFunder>> {
163 self.fee_funder.as_ref()
164 }
165}
166
167impl From<Client<FilesystemKeyStore>> for TestClient {
168 fn from(client: Client<FilesystemKeyStore>) -> Self {
169 Self::new(client)
170 }
171}
172
173impl Deref for TestClient {
174 type Target = Client<FilesystemKeyStore>;
175
176 fn deref(&self) -> &Self::Target {
177 &self.client
178 }
179}
180
181impl DerefMut for TestClient {
182 fn deref_mut(&mut self) -> &mut Self::Target {
183 &mut self.client
184 }
185}
186
187enum StandardComponents {
192 Wallet,
194 Faucet,
198}
199
200enum AccountKind {
202 Standard {
204 components: StandardComponents,
205 account_type: AccountType,
206 auth_scheme: AuthSchemeId,
207 },
208 Prebuilt {
210 account: Box<Account>,
211 key: AuthSecretKey,
212 },
213}
214
215pub struct AccountSetup {
217 kind: AccountKind,
218 funded: bool,
219 invitation_code: Option<String>,
220}
221
222impl AccountSetup {
223 fn standard(components: StandardComponents, account_type: AccountType) -> Self {
224 Self {
225 kind: AccountKind::Standard {
226 components,
227 account_type,
228 auth_scheme: ECDSA_K256_KECCAK_SCHEME_ID,
229 },
230 funded: true,
231 invitation_code: None,
232 }
233 }
234
235 pub fn wallet(account_type: AccountType) -> Self {
237 Self::standard(StandardComponents::Wallet, account_type)
238 }
239
240 pub fn faucet(account_type: AccountType) -> Self {
242 Self::standard(StandardComponents::Faucet, account_type)
243 }
244
245 pub fn prebuilt(account: Account, key: AuthSecretKey) -> Self {
247 Self {
248 kind: AccountKind::Prebuilt { account: Box::new(account), key },
249 funded: true,
250 invitation_code: None,
251 }
252 }
253
254 #[must_use]
256 pub fn auth_scheme(mut self, auth_scheme: AuthSchemeId) -> Self {
257 if let AccountKind::Standard { auth_scheme: scheme, .. } = &mut self.kind {
258 *scheme = auth_scheme;
259 }
260 self
261 }
262
263 #[must_use]
265 pub fn unfunded(mut self) -> Self {
266 self.funded = false;
267 self
268 }
269
270 #[must_use]
272 pub fn invitation_code(mut self, invitation_code: &str) -> Self {
273 self.invitation_code = Some(invitation_code.to_string());
274 self
275 }
276}
277
278pub fn auth_component(auth_scheme: AuthSchemeId) -> Result<(AuthSingleSig, AuthSecretKey)> {
280 let key_pair = match auth_scheme {
281 AuthSchemeId::Falcon512Poseidon2 => AuthSecretKey::new_falcon512_poseidon2(),
282 AuthSchemeId::EcdsaK256Keccak => AuthSecretKey::new_ecdsa_k256_keccak(),
283 other => anyhow::bail!("unsupported auth scheme: {}", other.as_u8()),
284 };
285 let component =
286 AuthSingleSig::new(Approver::new(key_pair.public_key().to_commitment(), auth_scheme));
287
288 Ok((component, key_pair))
289}
290
291pub fn fungible_faucet_component() -> Result<(FungibleFaucet, TokenPolicyManager)> {
294 let symbol = TokenSymbol::new("TEST").expect("TEST is a valid token symbol");
295 let name = TokenName::new(&symbol.to_string()).expect("token symbol is a valid token name");
296 let max_supply = 9_999_999_u64;
297 let faucet = FungibleFaucet::builder()
298 .name(name)
299 .symbol(symbol)
300 .decimals(10)
301 .max_supply(AssetAmount::new(max_supply).expect("max supply is a valid amount"))
302 .build()
303 .context("failed to build the fungible faucet component")?;
304
305 let policy_manager = TokenPolicyManager::builder()
306 .active_mint_policy(MintPolicy::allow_all())
307 .active_burn_policy(BurnPolicy::allow_all())
308 .build();
309
310 Ok((faucet, policy_manager))
311}
312
313impl TestClient {
314 pub async fn insert_account(
318 &mut self,
319 setup: AccountSetup,
320 ) -> Result<(Account, AuthSecretKey)> {
321 let (account, key_pair) = match setup.kind {
322 AccountKind::Prebuilt { account, key } => (*account, key),
323 AccountKind::Standard { components, account_type, auth_scheme } => {
324 let (auth, key_pair) = auth_component(auth_scheme)?;
325
326 let mut init_seed = [0u8; 32];
327 self.rng().fill_bytes(&mut init_seed);
328
329 let mut builder =
330 AccountBuilder::new(init_seed).account_type(account_type).with_component(auth);
331
332 match components {
333 StandardComponents::Wallet => {
334 builder = builder.with_component(BasicWallet);
335 },
336 StandardComponents::Faucet => {
337 let (faucet, policy_manager) = fungible_faucet_component()?;
338 builder = builder
339 .with_component(faucet)
340 .with_component(BasicWallet)
341 .with_components(policy_manager);
342 },
343 }
344
345 let account = builder
346 .build_with_schema_commitment()
347 .context("failed to build the test account")?;
348
349 (account, key_pair)
350 },
351 };
352
353 self.keystore()
354 .add_key(&key_pair, account.id())
355 .await
356 .context("failed to add the account key to the keystore")?;
357
358 self.add_account(&account, false).await?;
359 if let Some(invitation_code) = setup.invitation_code.as_deref() {
360 self.register_account(account.id(), invitation_code).await?;
361 }
362
363 info!(
364 account_id = %account.id(),
365 account_type = ?account.id().account_type(),
366 "Inserted account"
367 );
368
369 if setup.funded {
370 self.fund_if_needed(&[account.id()]).await?;
371 }
372
373 Ok((account, key_pair))
374 }
375
376 pub async fn insert_wallet(&mut self, account_type: AccountType) -> Result<Account> {
378 let (account, _) = self.insert_account(AccountSetup::wallet(account_type)).await?;
379 Ok(account)
380 }
381
382 pub async fn insert_faucet(&mut self, account_type: AccountType) -> Result<Account> {
384 let (account, _) = self.insert_account(AccountSetup::faucet(account_type)).await?;
385 Ok(account)
386 }
387
388 pub async fn setup_wallet_and_faucet(
390 &mut self,
391 account_type: AccountType,
392 ) -> Result<(Account, Account)> {
393 let (faucet_account, _) = self
394 .insert_account(AccountSetup::faucet(account_type).unfunded())
395 .await
396 .context("failed to insert new fungible faucet account")?;
397
398 let (basic_account, _) = self
399 .insert_account(AccountSetup::wallet(account_type).unfunded())
400 .await
401 .context("failed to insert new wallet account")?;
402
403 self.fund_if_needed(&[faucet_account.id(), basic_account.id()])
404 .await
405 .context("failed to fund and deploy the created accounts")?;
406
407 Ok((basic_account, faucet_account))
408 }
409
410 pub async fn setup_two_wallets_and_faucet(
413 &mut self,
414 account_type: AccountType,
415 ) -> Result<(Account, Account, Account)> {
416 let account_headers = self
418 .get_account_headers()
419 .await
420 .with_context(|| "failed to get account headers")?;
421 anyhow::ensure!(
422 account_headers.is_empty(),
423 "Expected empty account headers for clean state"
424 );
425
426 let transactions = self
427 .get_transactions(TransactionFilter::All)
428 .await
429 .with_context(|| "failed to get transactions")?;
430 anyhow::ensure!(transactions.is_empty(), "Expected empty transactions for clean state");
431
432 let input_notes = self
433 .get_input_notes(NoteFilter::All)
434 .await
435 .with_context(|| "failed to get input notes")?;
436 anyhow::ensure!(input_notes.is_empty(), "Expected empty input notes for clean state");
437
438 let (faucet_account, _) = self
439 .insert_account(AccountSetup::faucet(account_type).unfunded())
440 .await
441 .context("failed to insert new fungible faucet account")?;
442
443 let (first_basic_account, _) = self
444 .insert_account(AccountSetup::wallet(account_type).unfunded())
445 .await
446 .context("failed to insert first basic wallet account")?;
447
448 let (second_basic_account, _) = self
449 .insert_account(AccountSetup::wallet(account_type).unfunded())
450 .await
451 .context("failed to insert second basic wallet account")?;
452
453 self.fund_if_needed(&[
454 faucet_account.id(),
455 first_basic_account.id(),
456 second_basic_account.id(),
457 ])
458 .await
459 .context("failed to fund and deploy the created accounts")?;
460
461 info!(
462 faucet_id = %faucet_account.id(),
463 wallet_1_id = %first_basic_account.id(),
464 wallet_2_id = %second_basic_account.id(),
465 "Setup complete, syncing state"
466 );
467 self.sync_state().await.with_context(|| "failed to sync client state")?;
468
469 Ok((first_basic_account, second_basic_account, faucet_account))
470 }
471}
472
473impl TestClient {
477 pub async fn execute_failing_tx(
479 &mut self,
480 account_id: AccountId,
481 tx_request: TransactionRequest,
482 expected_error: ClientError,
483 ) {
484 info!(account_id = %account_id, "Executing transaction (expecting failure)");
485 assert_eq!(
487 self.submit_new_transaction(account_id, tx_request)
488 .await
489 .unwrap_err()
490 .to_string(),
491 expected_error.to_string()
492 );
493 }
494
495 pub async fn execute_tx_and_sync(
497 &mut self,
498 account_id: AccountId,
499 tx_request: TransactionRequest,
500 ) -> Result<()> {
501 let transaction_id = self.submit_new_transaction(account_id, tx_request).await?;
502 info!(tx_id = %transaction_id, account_id = %account_id, "Transaction submitted, waiting for commit");
503 self.wait_for_tx(transaction_id).await?;
504 Ok(())
505 }
506
507 pub async fn wait_for_tx(&mut self, transaction_id: TransactionId) -> Result<()> {
509 let now = Instant::now();
511 debug!(tx_id = %transaction_id, "Waiting for transaction to be committed");
512 loop {
513 self.sync_state()
514 .await
515 .with_context(|| "failed to sync client state while waiting for transaction")?;
516
517 let tracked_transaction = self
519 .get_transactions(TransactionFilter::Ids(vec![transaction_id]))
520 .await
521 .with_context(|| format!("failed to get transaction with ID: {transaction_id}"))?
522 .pop()
523 .with_context(|| format!("transaction with ID {transaction_id} not found"))?;
524
525 match tracked_transaction.status {
526 TransactionStatus::Committed { block_number, .. } => {
527 info!(tx_id = %transaction_id, %block_number, "Transaction committed");
528 break;
529 },
530 TransactionStatus::Pending => {
531 tokio::time::sleep(Duration::from_millis(500)).await;
534 },
535 TransactionStatus::Discarded(cause) => {
536 anyhow::bail!("transaction was discarded with cause: {cause:?}");
537 },
538 }
539
540 if std::env::var("LOG_WAIT_TIMES") == Ok("true".to_string()) {
543 let elapsed = now.elapsed();
544 let wait_times_dir = std::path::PathBuf::from("wait_times");
545 std::fs::create_dir_all(&wait_times_dir)
546 .with_context(|| "failed to create wait_times directory")?;
547
548 let elapsed_time_file =
549 wait_times_dir.join(format!("wait_time_{}", Uuid::new_v4()));
550 let mut file = OpenOptions::new()
551 .create(true)
552 .write(true)
553 .truncate(true)
554 .open(elapsed_time_file)
555 .with_context(|| "failed to create elapsed time file")?;
556 writeln!(file, "{:?}", elapsed.as_millis())
557 .with_context(|| "failed to write elapsed time to file")?;
558 }
559 }
560 Ok(())
561 }
562
563 pub async fn wait_for_blocks(&mut self, amount_of_blocks: u32) -> Result<SyncSummary> {
565 let current_block = self.get_sync_height().await?;
566 let final_block = current_block + amount_of_blocks;
567 debug!(current_block = %current_block, target_block = %final_block, "Waiting for blocks");
568 loop {
569 let summary = self.sync_state().await?;
570 debug!(sync_height = %summary.block_num, target_block = %final_block, "Synced");
571
572 if summary.block_num >= final_block {
573 return Ok(summary);
574 }
575
576 tokio::time::sleep(Duration::from_secs(3)).await;
577 }
578 }
579
580 pub async fn wait_for_blocks_no_sync(&mut self, amount_of_blocks: u32) -> Result<()> {
583 let current_block = self.get_sync_height().await?;
584 let final_block = current_block + amount_of_blocks;
585 debug!(current_block = %current_block, target_block = %final_block, "Waiting for blocks (no sync)");
586 loop {
587 let (latest_block, _) =
588 self.test_rpc_api().get_block_header_by_number(None, false).await?;
589 debug!(
590 chain_tip = %latest_block.block_num(),
591 target_block = %final_block,
592 "Waiting for blocks (no sync)"
593 );
594
595 if latest_block.block_num() >= final_block {
596 return Ok(());
597 }
598
599 tokio::time::sleep(Duration::from_secs(3)).await;
600 }
601 }
602
603 pub async fn wait_for_consumable_notes(
606 &mut self,
607 account_id: AccountId,
608 max_blocks: u32,
609 ) -> Result<Vec<(InputNoteRecord, Vec<NoteConsumability>)>> {
610 let start_block = self.get_sync_height().await?;
611 let deadline_block = start_block + max_blocks;
612 debug!(
613 %account_id,
614 %start_block,
615 %deadline_block,
616 "Waiting for consumable notes"
617 );
618
619 loop {
620 self.sync_state().await?;
621 let notes = self.get_consumable_notes(Some(account_id)).await?;
622 if !notes.is_empty() {
623 let current_block = self.get_sync_height().await?;
624 debug!(
625 %account_id,
626 count = notes.len(),
627 %current_block,
628 "Found consumable notes"
629 );
630 return Ok(notes);
631 }
632
633 let current_block = self.get_sync_height().await?;
634 assert!(
635 current_block < deadline_block,
636 "account {account_id} has no consumable notes after waiting {max_blocks} blocks \
637 (from block {start_block} to {current_block})"
638 );
639
640 debug!(
641 %account_id,
642 %current_block,
643 %deadline_block,
644 "No consumable notes yet, waiting..."
645 );
646 std::thread::sleep(Duration::from_secs(3));
647 }
648 }
649
650 pub async fn wait_for_node(&mut self) {
652 const NODE_TIME_BETWEEN_ATTEMPTS: u64 = 2;
653 const NUMBER_OF_NODE_ATTEMPTS: u64 = 60;
654 info!(
655 "Waiting for node to be up (checking every {NODE_TIME_BETWEEN_ATTEMPTS}s, max {NUMBER_OF_NODE_ATTEMPTS} tries)"
656 );
657 for _try_number in 0..NUMBER_OF_NODE_ATTEMPTS {
658 match self.sync_state().await {
659 Err(ClientError::RpcError(
660 RpcError::ConnectionError(_) | RpcError::RequestError { .. },
661 )) => {
662 tokio::time::sleep(Duration::from_secs(NODE_TIME_BETWEEN_ATTEMPTS)).await;
663 },
664 Err(other_error) => {
665 panic!("Unexpected error: {other_error}");
666 },
667 _ => return,
668 }
669 }
670
671 panic!("Unable to connect to node");
672 }
673
674 pub async fn mint_note(
677 &mut self,
678 basic_account_id: AccountId,
679 faucet_account_id: AccountId,
680 note_type: NoteType,
681 ) -> Result<(TransactionId, Note)> {
682 let fungible_asset = FungibleAsset::new(faucet_account_id, MINT_AMOUNT)?;
684 info!(faucet_id = %faucet_account_id, target_id = %basic_account_id, amount = MINT_AMOUNT, "Minting asset");
685 let tx_request = TransactionRequestBuilder::new().build_mint_fungible_asset(
686 fungible_asset,
687 basic_account_id,
688 note_type,
689 self.rng(),
690 )?;
691 let tx_id = self
692 .submit_new_transaction(fungible_asset.faucet_id(), tx_request.clone())
693 .await?;
694
695 let note = tx_request
696 .expected_output_own_notes()
697 .pop()
698 .context("the mint request should produce one output note")?;
699 info!(tx_id = %tx_id, note_id = %note.id(), "Mint transaction submitted");
700 Ok((tx_id, note))
701 }
702
703 pub async fn consume_notes(
706 &mut self,
707 account_id: AccountId,
708 input_notes: &[Note],
709 ) -> Result<TransactionId> {
710 let note_ids: Vec<_> = input_notes.iter().map(|n| n.id().to_string()).collect();
711 info!(account_id = %account_id, note_ids = %note_ids.join(", "), "Consuming notes");
712 let tx_request =
713 TransactionRequestBuilder::new().build_consume_notes(input_notes.to_vec())?;
714 let tx_id = self.submit_new_transaction(account_id, tx_request).await?;
715 info!(tx_id = %tx_id, "Consume transaction submitted");
716 Ok(tx_id)
717 }
718
719 pub async fn execute_tx_and_consume_output_notes(
722 &mut self,
723 tx_request: TransactionRequest,
724 executor: AccountId,
725 consumer: AccountId,
726 ) -> Result<TransactionId> {
727 let output_notes = tx_request
728 .expected_output_own_notes()
729 .into_iter()
730 .map(|note| (note, None::<NoteArgs>))
731 .collect::<Vec<(Note, Option<NoteArgs>)>>();
732
733 self.submit_new_transaction(executor, tx_request).await?;
734
735 let tx_request = TransactionRequestBuilder::new().input_notes(output_notes).build()?;
736 Ok(self.submit_new_transaction(consumer, tx_request).await?)
737 }
738
739 pub async fn mint_and_consume(
742 &mut self,
743 basic_account_id: AccountId,
744 faucet_account_id: AccountId,
745 note_type: NoteType,
746 ) -> Result<TransactionId> {
747 info!(
748 faucet_id = %faucet_account_id,
749 target_id = %basic_account_id,
750 amount = MINT_AMOUNT,
751 "Minting and consuming asset"
752 );
753 let tx_request = TransactionRequestBuilder::new().build_mint_fungible_asset(
754 FungibleAsset::new(faucet_account_id, MINT_AMOUNT)?,
755 basic_account_id,
756 note_type,
757 self.rng(),
758 )?;
759
760 let tx_id = self
761 .execute_tx_and_consume_output_notes(tx_request, faucet_account_id, basic_account_id)
762 .await?;
763 info!(tx_id = %tx_id, "Mint-and-consume transaction submitted");
764 Ok(tx_id)
765 }
766
767 pub fn mint_multiple_fungible_asset(
769 &mut self,
770 asset: FungibleAsset,
771 target_id: &[AccountId],
772 note_type: NoteType,
773 ) -> Result<TransactionRequest> {
774 let rng = self.rng();
775 let notes = target_id
776 .iter()
777 .map(|account_id| {
778 Ok(P2idNote::builder()
779 .sender(asset.faucet_id())
780 .target(*account_id)
781 .asset(asset)
782 .note_type(note_type)
783 .generate_serial_number(rng)
784 .build()
785 .context("note creation failed")?
786 .into())
787 })
788 .collect::<Result<Vec<Note>>>()?;
789
790 Ok(TransactionRequestBuilder::new().own_output_notes(notes).build()?)
791 }
792}
793
794impl TestClient {
798 pub async fn assert_account_has_single_asset(
800 &self,
801 account_id: AccountId,
802 faucet_id: AccountId,
803 expected_amount: u64,
804 ) {
805 let balance = self
806 .account_reader(account_id)
807 .get_balance(faucet_id)
808 .await
809 .expect("Account should have the asset");
810 assert_eq!(balance, AssetAmount::new(expected_amount).unwrap());
811 }
812
813 pub async fn assert_note_cannot_be_consumed_twice(
815 &mut self,
816 consuming_account_id: AccountId,
817 note_to_consume: Note,
818 ) {
819 info!(note_id = %note_to_consume.id(), account_id = %consuming_account_id, "Attempting double-consume (expecting failure)");
821
822 let tx_request = TransactionRequestBuilder::new()
824 .build_consume_notes(vec![note_to_consume.clone()])
825 .unwrap();
826
827 match self.submit_new_transaction(consuming_account_id, tx_request).await {
828 Err(ClientError::TransactionRequestError(
829 TransactionRequestError::InputNoteAlreadyConsumed(_),
830 )) => {},
831 Ok(_) => panic!("Double-spend error: Note should not be consumable!"),
832 err => {
833 panic!("Unexpected error {:?} for note ID: {}", err, note_to_consume.id().to_hex())
834 },
835 }
836 }
837}
838
839pub const ACCOUNT_ID_REGULAR: u128 = ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE;
843
844pub const RECALL_HEIGHT_DELTA: u32 = 50;
847
848pub const MINT_AMOUNT: u64 = 1000;
849pub const TRANSFER_AMOUNT: u64 = 59;
850
851pub fn create_test_store_path() -> PathBuf {
855 let mut temp_file = temp_dir();
856 temp_file.push(format!("{}.sqlite3", Uuid::new_v4()));
857 temp_file
858}