1use std::{
20 collections::HashMap,
21 io::{Cursor, Write},
22};
23
24use darkfi::{
25 blockchain::{BlockInfo, BlockchainOverlay},
26 runtime::vm_runtime::Runtime,
27 tx::Transaction,
28 util::{pcg::Pcg32, time::Timestamp},
29 validator::{Validator, ValidatorConfig, ValidatorPtr},
30 zk::{empty_witnesses, halo2::Field, ProvingKey, ZkCircuit},
31 zkas::ZkBinary,
32 Result,
33};
34use darkfi_dao_contract::model::{DaoBulla, DaoProposalBulla};
35use darkfi_money_contract::client::OwnCoin;
36use darkfi_sdk::{
37 bridgetree,
38 crypto::{
39 smt::{MemoryStorageFp, PoseidonFp, SmtMemoryFp, EMPTY_NODES_FP},
40 Keypair, MerkleNode, MerkleTree,
41 },
42 pasta::pallas,
43};
44use darkfi_serial::Encodable;
45use log::debug;
46use num_bigint::BigUint;
47use sled_overlay::sled;
48
49pub mod vks;
51
52mod money_pow_reward;
54
55mod money_fee;
57
58mod money_genesis_mint;
60
61mod money_transfer;
63
64mod money_token;
66
67mod money_otc_swap;
69
70mod contract_deploy;
72
73mod dao_mint;
75
76mod dao_propose;
78
79mod dao_vote;
81
82mod dao_exec;
84
85pub fn init_logger() {
87 let mut cfg = simplelog::ConfigBuilder::new();
88 cfg.add_filter_ignore("sled".to_string());
89 if simplelog::TermLogger::init(
94 simplelog::LevelFilter::Info,
95 cfg.build(),
98 simplelog::TerminalMode::Mixed,
99 simplelog::ColorChoice::Auto,
100 )
101 .is_err()
102 {
103 debug!(target: "test_harness", "Logger initialized");
104 }
105}
106
107#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
109pub enum Holder {
110 Alice,
111 Bob,
112 Charlie,
113 Dao,
114 Rachel,
115}
116
117pub struct Wallet {
119 pub keypair: Keypair,
121 pub token_mint_authority: Keypair,
123 pub contract_deploy_authority: Keypair,
125 pub validator: ValidatorPtr,
127 pub money_merkle_tree: MerkleTree,
129 pub money_null_smt: SmtMemoryFp,
131 pub money_null_smt_snapshot: Option<SmtMemoryFp>,
133 pub dao_merkle_tree: MerkleTree,
135 pub dao_proposals_tree: MerkleTree,
137 pub unspent_money_coins: Vec<OwnCoin>,
139 pub spent_money_coins: Vec<OwnCoin>,
141 pub dao_leafs: HashMap<DaoBulla, bridgetree::Position>,
143 pub dao_prop_leafs: HashMap<DaoProposalBulla, (bridgetree::Position, MerkleTree)>,
145 pub bench_wasm: bool,
147}
148
149impl Wallet {
150 pub async fn new(
152 keypair: Keypair,
153 token_mint_authority: Keypair,
154 contract_deploy_authority: Keypair,
155 genesis_block: BlockInfo,
156 vks: &vks::Vks,
157 verify_fees: bool,
158 ) -> Result<Self> {
159 let sled_db = sled::Config::new().temporary(true).open()?;
161
162 vks::inject(&sled_db, vks)?;
164
165 let validator_config = ValidatorConfig {
167 confirmation_threshold: 3,
168 pow_target: 90,
169 pow_fixed_difficulty: Some(BigUint::from(1_u8)),
170 genesis_block,
171 verify_fees,
172 };
173 let validator = Validator::new(&sled_db, &validator_config).await?;
174
175 let mut money_merkle_tree = MerkleTree::new(1);
178 money_merkle_tree.append(MerkleNode::from(pallas::Base::ZERO));
179 money_merkle_tree.mark().unwrap();
180
181 let hasher = PoseidonFp::new();
182 let store = MemoryStorageFp::new();
183 let money_null_smt = SmtMemoryFp::new(store, hasher, &EMPTY_NODES_FP);
184
185 Ok(Self {
186 keypair,
187 token_mint_authority,
188 contract_deploy_authority,
189 validator,
190 money_merkle_tree,
191 money_null_smt,
192 money_null_smt_snapshot: None,
193 dao_merkle_tree: MerkleTree::new(1),
194 dao_proposals_tree: MerkleTree::new(1),
195 unspent_money_coins: vec![],
196 spent_money_coins: vec![],
197 dao_leafs: HashMap::new(),
198 dao_prop_leafs: HashMap::new(),
199 bench_wasm: false,
200 })
201 }
202
203 pub async fn add_transaction(
204 &mut self,
205 callname: &str,
206 tx: Transaction,
207 block_height: u32,
208 ) -> Result<()> {
209 if self.bench_wasm {
210 benchmark_wasm_calls(callname, &self.validator, &tx, block_height).await;
211 }
212
213 self.validator
214 .add_test_transactions(
215 &[tx.clone()],
216 block_height,
217 self.validator.consensus.module.read().await.target,
218 true,
219 self.validator.verify_fees,
220 )
221 .await?;
222
223 {
225 let blockchain = &self.validator.blockchain;
226 let txs = &blockchain.transactions;
227 txs.insert(&[tx.clone()]).expect("insert tx");
228 txs.insert_location(&[tx.hash()], block_height).expect("insert loc");
229 }
230
231 Ok(())
232 }
233}
234
235pub struct TestHarness {
237 pub holders: HashMap<Holder, Wallet>,
239 pub proving_keys: HashMap<String, (ProvingKey, ZkBinary)>,
241 pub genesis_block: BlockInfo,
243 pub verify_fees: bool,
245}
246
247impl TestHarness {
248 pub async fn new(holders: &[Holder], verify_fees: bool) -> Result<Self> {
251 let mut genesis_block = BlockInfo::default();
253 genesis_block.header.timestamp = Timestamp::from_u64(1689772567);
254 let producer_tx = genesis_block.txs.pop().unwrap();
255 genesis_block.append_txs(vec![producer_tx]);
256
257 let mut rng = Pcg32::new(42);
259
260 let (pks, vks) = vks::get_cached_pks_and_vks()?;
262 let mut proving_keys = HashMap::new();
263 for (bincode, namespace, pk) in pks {
264 let mut reader = Cursor::new(pk);
265 let zkbin = ZkBinary::decode(&bincode)?;
266 let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
267 let proving_key = ProvingKey::read(&mut reader, circuit)?;
268 proving_keys.insert(namespace, (proving_key, zkbin));
269 }
270
271 let mut holders_map = HashMap::new();
273 for holder in holders {
274 let keypair = Keypair::random(&mut rng);
275 let token_mint_authority = Keypair::random(&mut rng);
276 let contract_deploy_authority = Keypair::random(&mut rng);
277
278 let wallet = Wallet::new(
279 keypair,
280 token_mint_authority,
281 contract_deploy_authority,
282 genesis_block.clone(),
283 &vks,
284 verify_fees,
285 )
286 .await?;
287
288 holders_map.insert(*holder, wallet);
289 }
290
291 Ok(Self { holders: holders_map, proving_keys, genesis_block, verify_fees })
292 }
293
294 pub fn assert_trees(&self, holders: &[Holder]) {
296 assert!(holders.len() > 1);
297 let mut wallets = vec![];
299 for holder in holders {
300 wallets.push(self.holders.get(holder).unwrap());
301 }
302 let wallet = wallets[0];
304 let money_root = wallet.money_merkle_tree.root(0).unwrap();
305 for wallet in &wallets[1..] {
306 assert!(money_root == wallet.money_merkle_tree.root(0).unwrap());
307 }
308 }
309}
310
311async fn benchmark_wasm_calls(
312 callname: &str,
313 validator: &Validator,
314 tx: &Transaction,
315 block_height: u32,
316) {
317 let mut file = std::fs::OpenOptions::new().create(true).append(true).open("bench.csv").unwrap();
318
319 for (idx, call) in tx.calls.iter().enumerate() {
320 let overlay = BlockchainOverlay::new(&validator.blockchain).expect("blockchain overlay");
321 let wasm = overlay.lock().unwrap().contracts.get(call.data.contract_id).unwrap();
322 let mut runtime = Runtime::new(
323 &wasm,
324 overlay.clone(),
325 call.data.contract_id,
326 block_height,
327 validator.consensus.module.read().await.target,
328 tx.hash(),
329 idx as u8,
330 )
331 .expect("runtime");
332
333 let mut payload = vec![];
335 tx.calls.encode(&mut payload).unwrap();
336
337 let mut times = [0; 3];
338 let now = std::time::Instant::now();
339 let _metadata = runtime.metadata(&payload).expect("metadata");
340 times[0] = now.elapsed().as_micros();
341
342 let now = std::time::Instant::now();
343 let update = runtime.exec(&payload).expect("exec");
344 times[1] = now.elapsed().as_micros();
345
346 let now = std::time::Instant::now();
347 runtime.apply(&update).expect("update");
348 times[2] = now.elapsed().as_micros();
349
350 writeln!(
351 file,
352 "{}, {}, {}, {}, {}, {}",
353 callname,
354 tx.hash(),
355 idx,
356 times[0],
357 times[1],
358 times[2]
359 )
360 .unwrap();
361 }
362}