harness.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{collections::HashMap, sync::Arc};
  19. use darkfi::{
  20. blockchain::{BlockInfo, Blockchain, BlockchainOverlay, Header, HeaderHash},
  21. net::Settings,
  22. rpc::jsonrpc::JsonSubscriber,
  23. system::sleep,
  24. tx::{ContractCallLeaf, TransactionBuilder},
  25. validator::{
  26. consensus::{Fork, Proposal},
  27. utils::deploy_native_contracts,
  28. verification::{apply_producer_transaction, verify_block},
  29. Validator, ValidatorConfig,
  30. },
  31. zk::{empty_witnesses, ProvingKey, ZkCircuit},
  32. Result,
  33. };
  34. use darkfi_contract_test_harness::vks;
  35. use darkfi_money_contract::{
  36. client::pow_reward_v1::PoWRewardCallBuilder, MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
  37. };
  38. use darkfi_sdk::{
  39. crypto::{
  40. keypair::{Keypair, Network},
  41. MerkleTree, MONEY_CONTRACT_ID,
  42. },
  43. ContractCall,
  44. };
  45. use darkfi_serial::Encodable;
  46. use kvdb_overlay::{Database, TempDir};
  47. use num_bigint::BigUint;
  48. use url::Url;
  49. use crate::{
  50. proto::{DarkfidP2pHandler, ProposalMessage},
  51. registry::DarkfiMinersRegistry,
  52. task::sync::sync_task,
  53. DarkfiNode, DarkfiNodePtr,
  54. };
  55. pub struct HarnessConfig {
  56. pub pow_target: u32,
  57. pub pow_fixed_difficulty: Option<BigUint>,
  58. pub confirmation_threshold: usize,
  59. pub max_forks: usize,
  60. pub alice_url: String,
  61. pub bob_url: String,
  62. }
  63. pub struct Harness {
  64. pub config: HarnessConfig,
  65. pub vks: Vec<(Vec<u8>, String, Vec<u8>)>,
  66. pub validator_config: ValidatorConfig,
  67. pub alice: DarkfiNodePtr,
  68. pub bob: DarkfiNodePtr,
  69. _temp_dirs: Vec<TempDir>,
  70. }
  71. impl Harness {
  72. pub async fn new(
  73. config: HarnessConfig,
  74. verify_fees: bool,
  75. ex: &Arc<smol::Executor<'static>>,
  76. ) -> Result<Self> {
  77. // Generate default genesis block
  78. let mut genesis_block = BlockInfo::default();
  79. // Retrieve genesis producer transaction
  80. let producer_tx = genesis_block.txs.pop().unwrap();
  81. // Append it again so its added to the merkle tree
  82. genesis_block.append_txs(vec![producer_tx]);
  83. // Compute genesis contracts states monotree root
  84. let (kvdb, _kvdb_folder) = Database::open_temp()?;
  85. let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
  86. let (_, vks) = vks::get_cached_pks_and_vks()?;
  87. vks::inject(&overlay, &vks)?;
  88. deploy_native_contracts(&overlay, config.pow_target).await?;
  89. let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
  90. genesis_block.header.state_root =
  91. overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
  92. // Generate validators configuration
  93. // NOTE: we are not using consensus constants here so we
  94. // don't get circular dependencies.
  95. let validator_config = ValidatorConfig {
  96. confirmation_threshold: config.confirmation_threshold,
  97. max_forks: config.max_forks,
  98. pow_target: config.pow_target,
  99. pow_fixed_difficulty: config.pow_fixed_difficulty.clone(),
  100. genesis_block,
  101. verify_fees,
  102. };
  103. // Generate validators
  104. let mut _temp_dirs = vec![];
  105. let mut settings = Settings {
  106. active_profiles: vec!["tcp+tls".to_string()],
  107. localnet: true,
  108. inbound_connections: 3,
  109. ..Default::default()
  110. };
  111. // Alice
  112. let alice_url = Url::parse(&config.alice_url)?;
  113. settings.inbound_addrs = vec![alice_url.clone()];
  114. let (alice, alice_folder) =
  115. generate_node(&vks, &validator_config, &settings, ex, true, None).await?;
  116. _temp_dirs.push(alice_folder);
  117. // Bob
  118. let bob_url = Url::parse(&config.bob_url)?;
  119. settings.inbound_addrs = vec![bob_url];
  120. settings.peers = vec![alice_url];
  121. let (bob, bob_folder) =
  122. generate_node(&vks, &validator_config, &settings, ex, false, None).await?;
  123. _temp_dirs.push(bob_folder);
  124. Ok(Self { config, vks, validator_config, alice, bob, _temp_dirs })
  125. }
  126. pub async fn validate_chains(&self, total_blocks: usize) -> Result<()> {
  127. let alice = &self.alice.validator.read().await;
  128. let bob = &self.bob.validator.read().await;
  129. alice
  130. .validate_blockchain(self.config.pow_target, self.config.pow_fixed_difficulty.clone())
  131. .await?;
  132. bob.validate_blockchain(self.config.pow_target, self.config.pow_fixed_difficulty.clone())
  133. .await?;
  134. let alice_blockchain_len = alice.blockchain.len()?;
  135. assert_eq!(alice_blockchain_len, bob.blockchain.len()?);
  136. assert_eq!(alice_blockchain_len, total_blocks);
  137. assert!(alice.blockchain.headers.is_empty_sync()?);
  138. assert!(bob.blockchain.headers.is_empty_sync()?);
  139. Ok(())
  140. }
  141. pub async fn validate_fork_chains(&self, total_forks: usize, fork_sizes: Vec<usize>) {
  142. let alice = &self.alice.validator.read().await.consensus.forks;
  143. let bob = &self.bob.validator.read().await.consensus.forks;
  144. let alice_forks_len = alice.len();
  145. assert_eq!(alice_forks_len, bob.len());
  146. assert_eq!(alice_forks_len, total_forks);
  147. for (index, fork) in alice.iter().enumerate() {
  148. assert_eq!(fork.proposals.len(), fork_sizes[index]);
  149. assert_eq!(fork.diffs.len(), fork_sizes[index]);
  150. assert!(fork.healthcheck().is_ok());
  151. }
  152. for (index, fork) in bob.iter().enumerate() {
  153. assert_eq!(fork.proposals.len(), fork_sizes[index]);
  154. assert_eq!(fork.diffs.len(), fork_sizes[index]);
  155. assert!(fork.healthcheck().is_ok());
  156. }
  157. }
  158. pub async fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
  159. // We append the block as a proposal to Alice,
  160. // and then we broadcast it to rest nodes
  161. for block in blocks {
  162. let proposal = Proposal::new(block.clone());
  163. self.alice.validator.write().await.append_proposal(&proposal, None).await?;
  164. let message = ProposalMessage(proposal);
  165. self.alice.p2p_handler.p2p.broadcast(&message).await?;
  166. }
  167. // Sleep a bit so blocks can be propagated and then
  168. // trigger confirmation check to Alice and Bob
  169. sleep(10).await;
  170. self.alice.validator.write().await.confirmation().await?;
  171. self.bob.validator.write().await.confirmation().await?;
  172. Ok(())
  173. }
  174. pub async fn generate_next_block(&self, fork: &mut Fork) -> Result<BlockInfo> {
  175. // Grab fork last block
  176. let previous = fork.overlay.lock().unwrap().last_block()?;
  177. // Next block info
  178. let block_height = previous.header.height + 1;
  179. let last_nonce = previous.header.nonce;
  180. // Generate a producer transaction
  181. let keypair = Keypair::default();
  182. let (zkbin, _) = fork
  183. .overlay
  184. .lock()
  185. .unwrap()
  186. .contracts
  187. .get_zkas(&MONEY_CONTRACT_ID, MONEY_CONTRACT_ZKAS_MINT_NS_V1)?;
  188. let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
  189. let pk = ProvingKey::build(zkbin.k, &circuit);
  190. // Build the transaction debris
  191. let debris = PoWRewardCallBuilder {
  192. signature_keypair: keypair,
  193. block_height,
  194. fees: 0,
  195. recipient: None,
  196. spend_hook: None,
  197. user_data: None,
  198. mint_zkbin: zkbin.clone(),
  199. mint_pk: pk.clone(),
  200. }
  201. .build()?;
  202. // Generate and sign the actual transaction
  203. let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
  204. debris.params.encode(&mut data)?;
  205. let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  206. let mut tx_builder =
  207. TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
  208. let mut tx = tx_builder.build()?;
  209. let sigs = tx.create_sigs(&[keypair.secret])?;
  210. tx.signatures = vec![sigs];
  211. // We increment timestamp so we don't have to use sleep
  212. let timestamp = previous.header.timestamp.checked_add(1.into())?;
  213. // Generate header
  214. let header = Header::new(previous.hash(), block_height, last_nonce, timestamp);
  215. // Generate the block
  216. let mut block = BlockInfo::new_empty(header);
  217. // Add producer transaction to the block
  218. block.append_txs(vec![tx]);
  219. // Compute block contracts states monotree root
  220. let overlay = fork.overlay.lock().unwrap().full_clone()?;
  221. let _ = apply_producer_transaction(
  222. &overlay,
  223. block.header.height,
  224. fork.module.target,
  225. block.txs.last().unwrap(),
  226. &mut MerkleTree::new(1),
  227. )
  228. .await?;
  229. let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&fork.diffs)?;
  230. block.header.state_root = overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
  231. // Attach signature
  232. block.sign(&keypair.secret);
  233. // Append new block to fork
  234. verify_block(
  235. &fork.overlay,
  236. &fork.diffs,
  237. &mut fork.module,
  238. &block,
  239. &previous,
  240. None,
  241. self.alice.validator.read().await.verify_fees,
  242. )
  243. .await?;
  244. fork.append_proposal(&Proposal::new(block.clone())).await?;
  245. Ok(block)
  246. }
  247. }
  248. // Note: This function should mirror `darkfid::Darkfid::init`
  249. pub async fn generate_node(
  250. vks: &Vec<(Vec<u8>, String, Vec<u8>)>,
  251. config: &ValidatorConfig,
  252. settings: &Settings,
  253. ex: &Arc<smol::Executor<'static>>,
  254. skip_sync: bool,
  255. checkpoint: Option<(u32, HeaderHash)>,
  256. ) -> Result<(DarkfiNodePtr, TempDir)> {
  257. let (kvdb, kvdb_folder) = Database::open_temp()?;
  258. let overlay = BlockchainOverlay::new(&Blockchain::new(&kvdb)?)?;
  259. vks::inject(&overlay, vks)?;
  260. deploy_native_contracts(&overlay, config.pow_target).await?;
  261. let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
  262. overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
  263. overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
  264. let validator = Validator::new(&kvdb, config).await?;
  265. let mut subscribers = HashMap::new();
  266. subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
  267. subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
  268. subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
  269. subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
  270. let p2p_handler = DarkfidP2pHandler::init(settings, ex).await?;
  271. let registry = DarkfiMinersRegistry::init(Network::Mainnet, &validator).await?;
  272. let node =
  273. DarkfiNode::new(validator.clone(), p2p_handler.clone(), registry, subscribers.clone())
  274. .await?;
  275. p2p_handler.start(ex, &node).await?;
  276. node.validator.write().await.consensus.generate_empty_fork().await?;
  277. if !skip_sync {
  278. sync_task(&node, checkpoint).await?;
  279. } else {
  280. node.validator.write().await.synced = true;
  281. }
  282. Ok((node, kvdb_folder))
  283. }