1use std::{
20 collections::HashMap,
21 fs::OpenOptions,
22 io::{Cursor, Write},
23 slice,
24 sync::Arc,
25 time::Instant,
26};
27
28use darkfi::{
29 blockchain::{BlockInfo, Blockchain, BlockchainOverlay},
30 runtime::vm_runtime::{Runtime, TxLocalState},
31 tx::Transaction,
32 util::{
33 logger::{setup_test_logger, Level},
34 pcg::Pcg32,
35 time::Timestamp,
36 },
37 validator::{utils::deploy_native_contracts, Validator, ValidatorConfig, ValidatorPtr},
38 zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
39 zkas::ZkBinary,
40 Result,
41};
42use darkfi_dao_contract::model::{Dao, DaoBulla, DaoProposal, DaoProposalBulla, DaoVoteParams};
43use darkfi_money_contract::{
44 client::{MoneyNote, OwnCoin},
45 model::{
46 CoinAttributes, Input, MoneyFeeParamsV1, MoneyGenesisMintParamsV1, Nullifier, Output,
47 TokenAttributes, TokenId,
48 },
49 MoneyFunction,
50};
51use darkfi_sdk::{
52 bridgetree,
53 crypto::{
54 contract_id::MONEY_CONTRACT_ID,
55 poseidon_hash,
56 smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
57 BaseBlind, FuncRef, Keypair, MerkleNode, MerkleTree, ScalarBlind, SecretKey,
58 },
59 pasta::pallas,
60};
61use darkfi_serial::{serialize, Encodable};
62use num_bigint::BigUint;
63use parking_lot::Mutex;
64use rand::rngs::OsRng;
65use sled_overlay::sled;
66use tracing::{debug, warn};
67
68pub mod vks;
70
71mod money_burn;
73mod money_fee;
75mod money_genesis_mint;
77mod money_otc_swap;
79mod money_pow_reward;
81mod money_token;
83mod money_transfer;
85
86mod contract_deploy;
88mod contract_lock;
90
91mod dao_exec;
93mod dao_mint;
95mod dao_propose;
97mod dao_vote;
99
100const POW_TARGET: u32 = 120;
102
103pub fn init_logger() {
105 if setup_test_logger(
108 &["sled"],
109 false,
110 Level::Info,
111 )
115 .is_err()
116 {
117 warn!(target: "test-harness", "Logger already initialized");
118 }
119}
120
121#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
123pub enum Holder {
124 Alice,
125 Bob,
126 Charlie,
127 Dao,
128 Rachel,
129}
130
131pub struct Wallet {
133 pub keypair: Keypair,
135 pub token_mint_authority: Keypair,
137 pub contract_deploy_authority: Keypair,
139 pub validator: ValidatorPtr,
141 pub money_merkle_tree: MerkleTree,
143 pub money_null_smt: SmtMemoryFp,
145 pub money_null_smt_snapshot: Option<SmtMemoryFp>,
148 pub dao_merkle_tree: MerkleTree,
151 pub dao_proposals_tree: MerkleTree,
154 pub unspent_money_coins: Vec<OwnCoin>,
156 pub spent_money_coins: Vec<OwnCoin>,
158 pub dao_leafs: HashMap<DaoBulla, bridgetree::Position>,
160 pub dao_prop_leafs: HashMap<DaoProposalBulla, (bridgetree::Position, MerkleTree)>,
162 pub bench_wasm: bool,
164}
165
166impl Wallet {
167 pub async fn new(
169 keypair: Keypair,
170 token_mint_authority: Keypair,
171 contract_deploy_authority: Keypair,
172 genesis_block: BlockInfo,
173 vks: &vks::Vks,
174 verify_fees: bool,
175 ) -> Result<Self> {
176 let sled_db = sled::Config::new().temporary(true).open()?;
178
179 let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
181 vks::inject(&overlay, vks)?;
182
183 deploy_native_contracts(&overlay, POW_TARGET).await?;
184 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
185 overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
186 overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
187
188 let validator_config = ValidatorConfig {
190 confirmation_threshold: 3,
191 max_forks: 8,
192 pow_target: POW_TARGET,
193 pow_fixed_difficulty: Some(BigUint::from(1_u8)),
194 genesis_block,
195 verify_fees,
196 };
197 let validator = Validator::new(&sled_db, &validator_config).await?;
198
199 let mut money_merkle_tree = MerkleTree::new(1);
202 money_merkle_tree.append(MerkleNode::from(pallas::Base::ZERO));
203 money_merkle_tree.mark().unwrap();
204
205 let hasher = PoseidonFp::new();
206 let store = MemoryStorageFp::new();
207 let money_null_smt = SmtMemoryFp::new(store, hasher, &EMPTY_NODES_FP);
208
209 Ok(Self {
210 keypair,
211 token_mint_authority,
212 contract_deploy_authority,
213 validator,
214 money_merkle_tree,
215 money_null_smt,
216 money_null_smt_snapshot: None,
217 dao_merkle_tree: MerkleTree::new(1),
218 dao_proposals_tree: MerkleTree::new(1),
219 unspent_money_coins: vec![],
220 spent_money_coins: vec![],
221 dao_leafs: HashMap::new(),
222 dao_prop_leafs: HashMap::new(),
223 bench_wasm: false,
224 })
225 }
226
227 pub async fn add_transaction(
228 &mut self,
229 callname: &str,
230 tx: Transaction,
231 block_height: u32,
232 ) -> Result<()> {
233 if self.bench_wasm {
234 let _ = benchmark_wasm_calls(callname, self.validator.clone(), &tx, block_height).await;
235 }
236
237 let validator = self.validator.read().await;
238 validator
239 .add_test_transactions(
240 slice::from_ref(&tx),
241 block_height,
242 validator.consensus.module.target,
243 true,
244 validator.verify_fees,
245 )
246 .await?;
247
248 let blockchain = &validator.blockchain;
250 let txs = &blockchain.transactions;
251 txs.insert(slice::from_ref(&tx)).expect("insert tx");
252 txs.insert_location(&[tx.hash()], block_height).expect("insert loc");
253
254 Ok(())
255 }
256
257 pub fn mark_spent_nullifier(&mut self, nullifier: Nullifier, holder: &Holder) {
260 let n = nullifier.inner();
261 self.money_null_smt.insert_batch(vec![(n, n)]).expect("smt.insert_batch()");
262
263 if let Some(spent_coin) =
264 self.unspent_money_coins.iter().find(|x| x.nullifier() == nullifier).cloned()
265 {
266 debug!("Found spent OwnCoin({}) for {:?}", spent_coin.coin, holder);
267 self.unspent_money_coins.retain(|x| x.nullifier() != nullifier);
268 self.spent_money_coins.push(spent_coin);
269 }
270 }
271
272 pub fn process_inputs(&mut self, inputs: &[Input], holder: &Holder) {
276 for input in inputs {
277 self.mark_spent_nullifier(input.nullifier, holder);
278 }
279 }
280
281 pub fn process_outputs(&mut self, outputs: &[Output], holder: &Holder) -> Vec<OwnCoin> {
285 let mut found = vec![];
286
287 for output in outputs {
288 if output.tx_local {
289 continue
290 }
291
292 self.money_merkle_tree.append(MerkleNode::from(output.coin.inner()));
293
294 let Ok(note) = output.note.decrypt::<MoneyNote>(&self.keypair.secret) else { continue };
295
296 let owncoin = OwnCoin {
297 coin: output.coin,
298 note: note.clone(),
299 secret: self.keypair.secret,
300 leaf_position: self.money_merkle_tree.mark().unwrap(),
301 };
302
303 debug!("Found new OwnCoin({}) for {:?}", owncoin.coin, holder);
304 self.unspent_money_coins.push(owncoin.clone());
305 found.push(owncoin);
306 }
307
308 found
309 }
310
311 pub fn process_fee(
315 &mut self,
316 fee_params: &Option<MoneyFeeParamsV1>,
317 holder: &Holder,
318 ) -> Vec<OwnCoin> {
319 let Some(ref fp) = fee_params else { return vec![] };
320
321 self.mark_spent_nullifier(fp.input.nullifier, holder);
322 self.process_outputs(slice::from_ref(&fp.output), holder)
323 }
324
325 pub fn validator(&self) -> &ValidatorPtr {
327 &self.validator
328 }
329}
330
331pub struct TestHarness {
333 pub holders: HashMap<Holder, Wallet>,
335 pub holder_keys: Vec<Holder>,
337 pub proving_keys: HashMap<String, (ProvingKey, ZkBinary)>,
339 pub genesis_block: BlockInfo,
341 pub verify_fees: bool,
343}
344
345impl TestHarness {
346 pub async fn new(holders: &[Holder], verify_fees: bool) -> Result<Self> {
349 let mut genesis_block = BlockInfo::default();
351 genesis_block.header.timestamp = Timestamp::from_u64(1689772567);
352 let producer_tx = genesis_block.txs.pop().unwrap();
353 genesis_block.append_txs(vec![producer_tx]);
354
355 let mut rng = Pcg32::new(42);
357
358 let (pks, vks) = vks::get_cached_pks_and_vks()?;
360 let mut proving_keys = HashMap::new();
361 for (bincode, namespace, pk) in pks {
362 let mut reader = Cursor::new(pk);
363 let zkbin = ZkBinary::decode(&bincode, false)?;
364 let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
365 let proving_key = ProvingKey::read(&mut reader, circuit)?;
366 proving_keys.insert(namespace, (proving_key, zkbin));
367 }
368
369 let sled_db = sled::Config::new().temporary(true).open()?;
371 let overlay = BlockchainOverlay::new(&Blockchain::new(&sled_db)?)?;
372 vks::inject(&overlay, &vks)?;
373 deploy_native_contracts(&overlay, POW_TARGET).await?;
374 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
375 genesis_block.header.state_root =
376 overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
377
378 let mut holders_map = HashMap::new();
380 let mut holder_keys = Vec::with_capacity(holders.len());
381
382 for holder in holders {
383 let keypair = Keypair::random(&mut rng);
384 let token_mint_authority = Keypair::random(&mut rng);
385 let contract_deploy_authority = Keypair::random(&mut rng);
386
387 let wallet = Wallet::new(
388 keypair,
389 token_mint_authority,
390 contract_deploy_authority,
391 genesis_block.clone(),
392 &vks,
393 verify_fees,
394 )
395 .await?;
396
397 holders_map.insert(*holder, wallet);
398 holder_keys.push(*holder);
399 }
400
401 Ok(Self { holders: holders_map, holder_keys, proving_keys, genesis_block, verify_fees })
402 }
403
404 pub fn wallet(&self, holder: &Holder) -> &Wallet {
406 self.holders.get(holder).unwrap()
407 }
408
409 pub fn wallet_mut(&mut self, holder: &Holder) -> &mut Wallet {
411 self.holders.get_mut(holder).unwrap()
412 }
413
414 pub fn coins(&self, holder: &Holder) -> &[OwnCoin] {
416 &self.wallet(holder).unspent_money_coins
417 }
418
419 pub fn coins_by_token(&self, holder: &Holder, token_id: TokenId) -> Vec<OwnCoin> {
421 self.coins(holder).iter().filter(|c| c.note.token_id == token_id).cloned().collect()
422 }
423
424 pub fn balance(&self, holder: &Holder, token_id: TokenId) -> u64 {
426 self.coins(holder)
427 .iter()
428 .filter(|c| c.note.token_id == token_id)
429 .map(|c| c.note.value)
430 .sum()
431 }
432
433 pub fn assert_trees(&self, holders: &[Holder]) {
435 assert!(!holders.is_empty());
436 let mut wallets = vec![];
437 for holder in holders {
438 wallets.push(self.holders.get(holder).unwrap());
439 }
440
441 let money_root = wallets[0].money_merkle_tree.root(0).unwrap();
442 for wallet in &wallets[1..] {
443 assert_eq!(money_root, wallet.money_merkle_tree.root(0).unwrap());
444 }
445 }
446
447 pub fn assert_all_trees(&self) {
449 if !self.holder_keys.is_empty() {
450 self.assert_trees(&self.holder_keys);
451 }
452 }
453
454 pub async fn token_mint_to_all(
458 &mut self,
459 amount: u64,
460 holder: &Holder,
461 recipient: &Holder,
462 block_height: u32,
463 ) -> Result<TokenId> {
464 let token_blind = BaseBlind::random(&mut OsRng);
465 let (tx, mint_params, auth_params, fee_params) = self
466 .token_mint(amount, holder, recipient, token_blind, None, None, block_height)
467 .await?;
468
469 let token_id = self.derive_token_id(recipient, token_blind);
471
472 let holders = self.holder_keys.clone();
473 for h in &holders {
474 self.execute_token_mint_tx(
475 h,
476 tx.clone(),
477 &mint_params,
478 &auth_params,
479 &fee_params,
480 block_height,
481 true,
482 )
483 .await?;
484 }
485
486 self.assert_all_trees();
487
488 Ok(token_id)
489 }
490
491 pub async fn token_mint_with_blind_to_all(
497 &mut self,
498 amount: u64,
499 holder: &Holder,
500 recipient: &Holder,
501 token_blind: BaseBlind,
502 block_height: u32,
503 ) -> Result<TokenId> {
504 let (tx, mint_params, auth_params, fee_params) = self
505 .token_mint(amount, holder, recipient, token_blind, None, None, block_height)
506 .await?;
507
508 let token_id = self.derive_token_id(recipient, token_blind);
509
510 let holders = self.holder_keys.clone();
511 for h in &holders {
512 self.execute_token_mint_tx(
513 h,
514 tx.clone(),
515 &mint_params,
516 &auth_params,
517 &fee_params,
518 block_height,
519 true,
520 )
521 .await?;
522 }
523 self.assert_all_trees();
524
525 Ok(token_id)
526 }
527
528 pub async fn transfer_to_all(
531 &mut self,
532 amount: u64,
533 sender: &Holder,
534 recipient: &Holder,
535 token_id: TokenId,
536 block_height: u32,
537 ) -> Result<()> {
538 let owncoins = self.coins_by_token(sender, token_id);
539 let (tx, (params, fee_params), _spent) = self
540 .transfer(amount, sender, recipient, &owncoins, token_id, block_height, false)
541 .await?;
542
543 let holders = self.holder_keys.clone();
544 for h in &holders {
545 self.execute_transfer_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
546 .await?;
547 }
548
549 self.assert_all_trees();
550
551 Ok(())
552 }
553
554 pub async fn burn_to_all(
556 &mut self,
557 holder: &Holder,
558 coins: &[OwnCoin],
559 block_height: u32,
560 ) -> Result<()> {
561 let (tx, (params, fee_params), _spent) = self.burn(holder, coins, block_height).await?;
562
563 let holders = self.holder_keys.clone();
564 for h in &holders {
565 self.execute_burn_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true).await?;
566 }
567
568 self.assert_all_trees();
569
570 Ok(())
571 }
572
573 pub async fn genesis_mint_to_all(
576 &mut self,
577 holder: &Holder,
578 amounts: &[u64],
579 block_height: u32,
580 ) -> Result<Vec<OwnCoin>> {
581 let (tx, params) = self.genesis_mint(holder, amounts, None, None).await?;
582 self.genesis_mint_to_all_with(tx, ¶ms, block_height).await
583 }
584
585 pub async fn genesis_mint_to_all_with(
589 &mut self,
590 tx: Transaction,
591 params: &MoneyGenesisMintParamsV1,
592 block_height: u32,
593 ) -> Result<Vec<OwnCoin>> {
594 let holders = self.holder_keys.clone();
595 let mut found = vec![];
596 for h in &holders {
597 found.extend(
598 self.execute_genesis_mint_tx(h, tx.clone(), params, block_height, true).await?,
599 );
600 }
601 self.assert_all_trees();
602 Ok(found)
603 }
604
605 pub async fn token_freeze_to_all(&mut self, holder: &Holder, block_height: u32) -> Result<()> {
608 let (tx, freeze_params, fee_params) = self.token_freeze(holder, block_height).await?;
609
610 let holders = self.holder_keys.clone();
611 for h in &holders {
612 self.execute_token_freeze_tx(
613 h,
614 tx.clone(),
615 &freeze_params,
616 &fee_params,
617 block_height,
618 true,
619 )
620 .await?;
621 }
622 self.assert_all_trees();
623 Ok(())
624 }
625
626 pub async fn generate_block_all(&mut self, miner: &Holder) -> Result<Vec<OwnCoin>> {
630 let holders = self.holder_keys.clone();
631 self.generate_block(miner, &holders).await
632 }
633
634 pub async fn consolidate_to_all(
637 &mut self,
638 holder: &Holder,
639 token_id: TokenId,
640 block_height: u32,
641 ) -> Result<()> {
642 let owncoins = self.coins_by_token(holder, token_id);
643 if owncoins.len() <= 1 {
644 return Ok(())
646 }
647
648 let total: u64 = owncoins.iter().map(|c| c.note.value).sum();
649 let (tx, (params, fee_params), _spent) =
650 self.transfer(total, holder, holder, &owncoins, token_id, block_height, false).await?;
651
652 let holders = self.holder_keys.clone();
653 for h in &holders {
654 self.execute_transfer_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
655 .await?;
656 }
657
658 self.assert_all_trees();
659
660 Ok(())
661 }
662
663 pub async fn otc_swap_to_all(
666 &mut self,
667 holder0: &Holder,
668 coin0: &OwnCoin,
669 holder1: &Holder,
670 coin1: &OwnCoin,
671 block_height: u32,
672 ) -> Result<()> {
673 let (tx, params, fee_params) =
674 self.otc_swap(holder0, coin0, holder1, coin1, block_height).await?;
675
676 let holders = self.holder_keys.clone();
677 for h in &holders {
678 self.execute_otc_swap_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
679 .await?;
680 }
681
682 self.assert_all_trees();
683
684 Ok(())
685 }
686
687 #[allow(clippy::too_many_arguments)]
689 pub async fn dao_mint_to_all(
690 &mut self,
691 holder: &Holder,
692 dao: &Dao,
693 dao_notes_secret_key: &SecretKey,
694 dao_proposer_secret_key: &SecretKey,
695 dao_proposals_secret_key: &SecretKey,
696 dao_votes_secret_key: &SecretKey,
697 dao_exec_secret_key: &SecretKey,
698 dao_early_exec_secret_key: &SecretKey,
699 block_height: u32,
700 ) -> Result<()> {
701 let (tx, params, fee_params) = self
702 .dao_mint(
703 holder,
704 dao,
705 dao_notes_secret_key,
706 dao_proposer_secret_key,
707 dao_proposals_secret_key,
708 dao_votes_secret_key,
709 dao_exec_secret_key,
710 dao_early_exec_secret_key,
711 block_height,
712 )
713 .await?;
714
715 let holders = self.holder_keys.clone();
716 for h in &holders {
717 self.execute_dao_mint_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
718 .await?;
719 }
720 self.assert_all_trees();
721 Ok(())
722 }
723
724 #[allow(clippy::too_many_arguments)]
728 pub async fn dao_propose_transfer_to_all(
729 &mut self,
730 proposer: &Holder,
731 proposal_coinattrs: &[CoinAttributes],
732 user_data: pallas::Base,
733 dao: &Dao,
734 dao_proposer_secret_key: &SecretKey,
735 block_height: u32,
736 duration_blockwindows: u64,
737 ) -> Result<DaoProposal> {
738 let (tx, params, fee_params, proposal_info) = self
739 .dao_propose_transfer(
740 proposer,
741 proposal_coinattrs,
742 user_data,
743 dao,
744 dao_proposer_secret_key,
745 block_height,
746 duration_blockwindows,
747 )
748 .await?;
749
750 let holders = self.holder_keys.clone();
751 for h in &holders {
752 self.execute_dao_propose_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
753 .await?;
754 }
755 self.assert_all_trees();
756 Ok(proposal_info)
757 }
758
759 pub async fn dao_propose_generic_to_all(
762 &mut self,
763 proposer: &Holder,
764 user_data: pallas::Base,
765 dao: &Dao,
766 dao_proposer_secret_key: &SecretKey,
767 block_height: u32,
768 duration_blockwindows: u64,
769 ) -> Result<DaoProposal> {
770 let (tx, params, fee_params, proposal_info) = self
771 .dao_propose_generic(
772 proposer,
773 user_data,
774 dao,
775 dao_proposer_secret_key,
776 block_height,
777 duration_blockwindows,
778 )
779 .await?;
780
781 let holders = self.holder_keys.clone();
782 for h in &holders {
783 self.execute_dao_propose_tx(h, tx.clone(), ¶ms, &fee_params, block_height, true)
784 .await?;
785 }
786 self.assert_all_trees();
787 Ok(proposal_info)
788 }
789
790 pub async fn dao_vote_to_all(
793 &mut self,
794 voter: &Holder,
795 vote_option: bool,
796 dao: &Dao,
797 proposal: &DaoProposal,
798 block_height: u32,
799 ) -> Result<DaoVoteParams> {
800 let (tx, vote_params, fee_params) =
801 self.dao_vote(voter, vote_option, dao, proposal, block_height).await?;
802
803 let holders = self.holder_keys.clone();
804 for h in &holders {
805 self.execute_dao_vote_tx(h, tx.clone(), &fee_params, block_height, true).await?;
806 }
807 self.assert_all_trees();
808 Ok(vote_params)
809 }
810
811 #[allow(clippy::too_many_arguments)]
814 pub async fn dao_exec_transfer_to_all(
815 &mut self,
816 executor: &Holder,
817 dao: &Dao,
818 dao_exec_secret_key: &SecretKey,
819 dao_early_exec_secret_key: &Option<SecretKey>,
820 proposal_info: &DaoProposal,
821 proposal_coinattrs: Vec<CoinAttributes>,
822 total_yes_vote_value: u64,
823 total_all_vote_value: u64,
824 total_yes_vote_blind: ScalarBlind,
825 total_all_vote_blind: ScalarBlind,
826 block_height: u32,
827 ) -> Result<()> {
828 let (tx, xfer_params, fee_params) = self
829 .dao_exec_transfer(
830 executor,
831 dao,
832 dao_exec_secret_key,
833 dao_early_exec_secret_key,
834 proposal_info,
835 proposal_coinattrs,
836 total_yes_vote_value,
837 total_all_vote_value,
838 total_yes_vote_blind,
839 total_all_vote_blind,
840 block_height,
841 )
842 .await?;
843
844 let holders = self.holder_keys.clone();
845 for h in &holders {
846 self.execute_dao_exec_tx(
847 h,
848 tx.clone(),
849 Some(&xfer_params),
850 &fee_params,
851 block_height,
852 true,
853 )
854 .await?;
855 }
856 self.assert_all_trees();
857 Ok(())
858 }
859
860 #[allow(clippy::too_many_arguments)]
863 pub async fn dao_exec_generic_to_all(
864 &mut self,
865 executor: &Holder,
866 dao: &Dao,
867 dao_exec_secret_key: &SecretKey,
868 dao_early_exec_secret_key: &Option<SecretKey>,
869 proposal_info: &DaoProposal,
870 total_yes_vote_value: u64,
871 total_all_vote_value: u64,
872 total_yes_vote_blind: ScalarBlind,
873 total_all_vote_blind: ScalarBlind,
874 block_height: u32,
875 ) -> Result<()> {
876 let (tx, fee_params) = self
877 .dao_exec_generic(
878 executor,
879 dao,
880 dao_exec_secret_key,
881 dao_early_exec_secret_key,
882 proposal_info,
883 total_yes_vote_value,
884 total_all_vote_value,
885 total_yes_vote_blind,
886 total_all_vote_blind,
887 block_height,
888 )
889 .await?;
890
891 let holders = self.holder_keys.clone();
892 for h in &holders {
893 self.execute_dao_exec_tx(h, tx.clone(), None, &fee_params, block_height, true).await?;
894 }
895 self.assert_all_trees();
896 Ok(())
897 }
898
899 pub fn derive_token_id(&self, holder: &Holder, token_blind: BaseBlind) -> TokenId {
902 let wallet = self.wallet(holder);
903 let mint_authority = wallet.token_mint_authority;
904
905 let auth_func_id = FuncRef {
906 contract_id: *MONEY_CONTRACT_ID,
907 func_code: MoneyFunction::AuthTokenMintV1 as u8,
908 }
909 .to_func_id();
910
911 let token_attrs = TokenAttributes {
912 auth_parent: auth_func_id,
913 user_data: poseidon_hash([mint_authority.public.x(), mint_authority.public.y()]),
914 blind: token_blind,
915 };
916
917 token_attrs.to_token_id()
918 }
919}
920
921async fn benchmark_wasm_calls(
922 callname: &str,
923 validator: ValidatorPtr,
924 tx: &Transaction,
925 block_height: u32,
926) -> Result<()> {
927 let mut file = OpenOptions::new().create(true).append(true).open("bench.csv")?;
928
929 let tx_local_state = Arc::new(Mutex::new(TxLocalState::new()));
930
931 let validator = validator.read().await;
932 for (idx, call) in tx.calls.iter().enumerate() {
933 let overlay = BlockchainOverlay::new(&validator.blockchain).expect("blockchain overlay");
934 let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id)?;
935
936 let mut runtime = Runtime::new(
937 &wasm,
938 overlay.clone(),
939 tx_local_state.clone(),
940 call.data.contract_id,
941 block_height,
942 validator.consensus.module.target,
943 tx.hash(),
944 idx as u8,
945 )
946 .expect("runtime");
947
948 let mut payload = vec![];
950 tx.calls.encode(&mut payload)?;
951
952 let mut times = [0; 3];
953 let now = Instant::now();
954 let _metadata = runtime.metadata(&payload)?;
955 times[0] = now.elapsed().as_micros();
956
957 let now = Instant::now();
958 let mut update = vec![call.data.data[0]];
959 update.append(&mut runtime.exec(&payload)?);
960 times[1] = now.elapsed().as_micros();
961
962 let now = Instant::now();
963 runtime.apply(&update)?;
964 times[2] = now.elapsed().as_micros();
965
966 writeln!(
967 file,
968 "{},{},{},{},{},{},{}",
969 callname,
970 tx.hash(),
971 idx,
972 times[0],
973 times[1],
974 times[2],
975 serialize(tx).len(),
976 )?;
977 }
978
979 Ok(())
980}