harness.rs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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, Header},
  21. net::Settings,
  22. rpc::jsonrpc::JsonSubscriber,
  23. tx::Transaction,
  24. util::time::TimeKeeper,
  25. validator::{pid::slot_pid_output, utils::genesis_txs_total, Validator, ValidatorConfig},
  26. Result,
  27. };
  28. use darkfi_contract_test_harness::{vks, Holder, TestHarness};
  29. use darkfi_sdk::{
  30. blockchain::{expected_reward, PidOutput, PreviousSlot, Slot, POS_START},
  31. pasta::{group::ff::Field, pallas},
  32. };
  33. use num_bigint::BigUint;
  34. use url::Url;
  35. use crate::{
  36. proto::BlockInfoMessage,
  37. task::sync::sync_task,
  38. utils::{spawn_consensus_p2p, spawn_sync_p2p},
  39. Darkfid,
  40. };
  41. pub struct HarnessConfig {
  42. pub pow_threads: usize,
  43. pub pow_target: usize,
  44. pub pow_fixed_difficulty: Option<BigUint>,
  45. pub pos_testing_mode: bool,
  46. pub alice_initial: u64,
  47. pub bob_initial: u64,
  48. }
  49. pub struct Harness {
  50. pub config: HarnessConfig,
  51. pub vks: Vec<(Vec<u8>, String, Vec<u8>)>,
  52. pub validator_config: ValidatorConfig,
  53. pub alice: Darkfid,
  54. pub bob: Darkfid,
  55. }
  56. impl Harness {
  57. pub async fn new(
  58. config: HarnessConfig,
  59. verify_fees: bool,
  60. ex: &Arc<smol::Executor<'static>>,
  61. ) -> Result<Self> {
  62. // Use test harness to generate genesis transactions
  63. let mut th =
  64. TestHarness::new(&["money".to_string(), "consensus".to_string()], verify_fees).await?;
  65. let (genesis_stake_tx, _) = th.genesis_stake(&Holder::Alice, config.alice_initial)?;
  66. let (genesis_mint_tx, _) = th.genesis_mint(&Holder::Bob, config.bob_initial)?;
  67. // Generate default genesis block
  68. let mut genesis_block = BlockInfo::default();
  69. // Append genesis transactions and calculate their total
  70. genesis_block.txs.push(genesis_stake_tx);
  71. genesis_block.txs.push(genesis_mint_tx);
  72. let genesis_txs_total = genesis_txs_total(&genesis_block.txs).await?;
  73. genesis_block.slots[0].total_tokens = genesis_txs_total;
  74. // Generate validators configuration
  75. // NOTE: we are not using consensus constants here so we
  76. // don't get circular dependencies.
  77. let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
  78. let validator_config = ValidatorConfig::new(
  79. time_keeper,
  80. 3,
  81. config.pow_threads,
  82. config.pow_target,
  83. config.pow_fixed_difficulty.clone(),
  84. genesis_block,
  85. genesis_txs_total,
  86. vec![],
  87. config.pos_testing_mode,
  88. verify_fees,
  89. );
  90. // Generate validators using pregenerated vks
  91. let (_, vks) = vks::read_or_gen_vks_and_pks()?;
  92. let mut sync_settings =
  93. Settings { localnet: true, inbound_connections: 3, ..Default::default() };
  94. let mut consensus_settings =
  95. Settings { localnet: true, inbound_connections: 3, ..Default::default() };
  96. // Alice
  97. let alice_url = Url::parse("tcp+tls://127.0.0.1:18340")?;
  98. sync_settings.inbound_addrs = vec![alice_url.clone()];
  99. let alice_consensus_url = Url::parse("tcp+tls://127.0.0.1:18350")?;
  100. consensus_settings.inbound_addrs = vec![alice_consensus_url.clone()];
  101. let alice = generate_node(
  102. &vks,
  103. &validator_config,
  104. &sync_settings,
  105. Some(&consensus_settings),
  106. ex,
  107. true,
  108. )
  109. .await?;
  110. // Bob
  111. let bob_url = Url::parse("tcp+tls://127.0.0.1:18341")?;
  112. sync_settings.inbound_addrs = vec![bob_url];
  113. sync_settings.peers = vec![alice_url];
  114. let bob_consensus_url = Url::parse("tcp+tls://127.0.0.1:18351")?;
  115. consensus_settings.inbound_addrs = vec![bob_consensus_url];
  116. consensus_settings.peers = vec![alice_consensus_url];
  117. let bob = generate_node(
  118. &vks,
  119. &validator_config,
  120. &sync_settings,
  121. Some(&consensus_settings),
  122. ex,
  123. false,
  124. )
  125. .await?;
  126. Ok(Self { config, vks, validator_config, alice, bob })
  127. }
  128. pub async fn validate_chains(&self, total_blocks: usize, total_slots: usize) -> Result<()> {
  129. let genesis_txs_total = self.config.alice_initial + self.config.bob_initial;
  130. let alice = &self.alice.validator;
  131. let bob = &self.bob.validator;
  132. alice
  133. .validate_blockchain(
  134. genesis_txs_total,
  135. vec![],
  136. self.config.pow_threads,
  137. self.config.pow_target,
  138. self.config.pow_fixed_difficulty.clone(),
  139. )
  140. .await?;
  141. bob.validate_blockchain(
  142. genesis_txs_total,
  143. vec![],
  144. self.config.pow_threads,
  145. self.config.pow_target,
  146. self.config.pow_fixed_difficulty.clone(),
  147. )
  148. .await?;
  149. let alice_blockchain_len = alice.blockchain.len();
  150. assert_eq!(alice_blockchain_len, bob.blockchain.len());
  151. assert_eq!(alice_blockchain_len, total_blocks);
  152. let alice_slots_len = alice.blockchain.slots.len();
  153. assert_eq!(alice_slots_len, bob.blockchain.slots.len());
  154. assert_eq!(alice_slots_len, total_slots);
  155. Ok(())
  156. }
  157. pub async fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
  158. // We simply broadcast the block using Alice's sync P2P
  159. for block in blocks {
  160. self.alice.sync_p2p.broadcast(&BlockInfoMessage::from(block)).await;
  161. }
  162. // and then add it to her chain
  163. self.alice.validator.add_blocks(blocks).await?;
  164. Ok(())
  165. }
  166. pub async fn generate_next_pos_block(
  167. &self,
  168. previous: &BlockInfo,
  169. slots_count: usize,
  170. ) -> Result<BlockInfo> {
  171. let previous_hash = previous.hash()?;
  172. // Generate empty slots
  173. let mut slots = Vec::with_capacity(slots_count);
  174. let mut previous_slot = previous.slots.last().unwrap().clone();
  175. for i in 0..slots_count {
  176. let id = if previous_slot.id < POS_START { POS_START } else { previous_slot.id + 1 };
  177. // First slot in the sequence has (at least) 1 previous slot producer
  178. let producers = if i == 0 { 1 } else { 0 };
  179. let previous = PreviousSlot::new(
  180. producers,
  181. vec![previous_hash],
  182. vec![previous.header.previous],
  183. previous_slot.pid.error,
  184. );
  185. let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
  186. let pid = PidOutput::new(f, error, sigma1, sigma2);
  187. let total_tokens = previous_slot.total_tokens + previous_slot.reward;
  188. // Only last slot in the sequence has a reward
  189. let reward = if i == slots_count - 1 { expected_reward(id) } else { 0 };
  190. let slot = Slot::new(id, previous, pid, pallas::Base::ZERO, total_tokens, reward);
  191. slots.push(slot.clone());
  192. previous_slot = slot;
  193. }
  194. // We increment timestamp so we don't have to use sleep
  195. let mut timestamp = previous.header.timestamp;
  196. timestamp.add(1);
  197. // Generate header
  198. let height = slots.last().unwrap().id;
  199. let header = Header::new(
  200. previous_hash,
  201. self.alice.validator.consensus.time_keeper.slot_epoch(height),
  202. height,
  203. timestamp,
  204. previous.header.nonce,
  205. );
  206. // Generate the block
  207. let mut block = BlockInfo::new_empty(header, slots);
  208. // Add transactions to the block
  209. block.append_txs(vec![Transaction::default()])?;
  210. // Attach signature
  211. block.signature = previous.signature;
  212. Ok(block)
  213. }
  214. }
  215. // Note: This function should mirror darkfid::main
  216. pub async fn generate_node(
  217. vks: &Vec<(Vec<u8>, String, Vec<u8>)>,
  218. config: &ValidatorConfig,
  219. sync_settings: &Settings,
  220. consensus_settings: Option<&Settings>,
  221. ex: &Arc<smol::Executor<'static>>,
  222. skip_sync: bool,
  223. ) -> Result<Darkfid> {
  224. let sled_db = sled::Config::new().temporary(true).open()?;
  225. vks::inject(&sled_db, vks)?;
  226. let validator = Validator::new(&sled_db, config.clone()).await?;
  227. let mut subscribers = HashMap::new();
  228. subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
  229. subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
  230. if consensus_settings.is_some() {
  231. subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
  232. }
  233. let sync_p2p = spawn_sync_p2p(sync_settings, &validator, &subscribers, ex.clone()).await;
  234. let consensus_p2p = if let Some(settings) = consensus_settings {
  235. Some(spawn_consensus_p2p(settings, &validator, &subscribers, ex.clone()).await)
  236. } else {
  237. None
  238. };
  239. let node = Darkfid::new(sync_p2p.clone(), consensus_p2p.clone(), validator, subscribers).await;
  240. sync_p2p.clone().start().await?;
  241. if consensus_settings.is_some() {
  242. let consensus_p2p = consensus_p2p.unwrap();
  243. consensus_p2p.clone().start().await?;
  244. }
  245. if !skip_sync {
  246. sync_task(&node).await?;
  247. } else {
  248. *node.validator.synced.write().await = true;
  249. }
  250. node.validator.purge_pending_txs().await?;
  251. Ok(node)
  252. }