harness.rs 8.8 KB

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