| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use async_std::sync::Arc;
- use darkfi::{
- blockchain::{BlockInfo, Header},
- net::Settings,
- util::time::TimeKeeper,
- validator::{
- consensus::{next_block_reward, pid::slot_pid_output},
- Validator, ValidatorConfig,
- },
- Result,
- };
- use darkfi_contract_test_harness::{vks, Holder, TestHarness};
- use darkfi_sdk::{
- blockchain::{PidOutput, PreviousSlot, Slot},
- pasta::{group::ff::Field, pallas},
- };
- use log::error;
- use url::Url;
- use crate::{
- task::sync::sync_task,
- utils::{genesis_txs_total, spawn_consensus_p2p, spawn_sync_p2p},
- Darkfid,
- };
- pub struct HarnessConfig {
- pub testing_node: bool,
- pub alice_initial: u64,
- pub bob_initial: u64,
- }
- pub struct Harness {
- pub config: HarnessConfig,
- pub vks: Vec<(Vec<u8>, String, Vec<u8>)>,
- pub validator_config: ValidatorConfig,
- pub alice: Darkfid,
- pub bob: Darkfid,
- }
- impl Harness {
- pub async fn new(config: HarnessConfig, ex: &Arc<smol::Executor<'_>>) -> Result<Self> {
- // Use test harness to generate genesis transactions
- let mut th = TestHarness::new(&["money".to_string(), "consensus".to_string()]).await?;
- let (genesis_stake_tx, _) = th.genesis_stake(&Holder::Alice, config.alice_initial)?;
- let (genesis_mint_tx, _) = th.genesis_mint(&Holder::Bob, config.bob_initial)?;
- // Generate default genesis block
- let mut genesis_block = BlockInfo::default();
- // Append genesis transactions and calculate their total
- genesis_block.txs.push(genesis_stake_tx);
- genesis_block.txs.push(genesis_mint_tx);
- let genesis_txs_total = genesis_txs_total(&genesis_block.txs)?;
- genesis_block.slots[0].total_tokens = genesis_txs_total;
- // Generate validators configuration
- // NOTE: we are not using consensus constants here so we
- // don't get circular dependencies.
- let time_keeper = TimeKeeper::new(genesis_block.header.timestamp, 10, 90, 0);
- let validator_config = ValidatorConfig::new(
- time_keeper,
- genesis_block,
- genesis_txs_total,
- vec![],
- config.testing_node,
- );
- // Generate validators using pregenerated vks
- let (_, vks) = vks::read_or_gen_vks_and_pks()?;
- let mut sync_settings = Settings::default();
- sync_settings.localnet = true;
- let mut consensus_settings = Settings::default();
- consensus_settings.localnet = true;
- // Alice
- let alice_url = Url::parse("tcp+tls://127.0.0.1:18340")?;
- sync_settings.inbound_addrs = vec![alice_url.clone()];
- let alice_consensus_url = Url::parse("tcp+tls://127.0.0.1:18350")?;
- consensus_settings.inbound_addrs = vec![alice_consensus_url.clone()];
- let alice = generate_node(
- &vks,
- &validator_config,
- &sync_settings,
- Some(&consensus_settings),
- ex,
- true,
- )
- .await?;
- // Bob
- let bob_url = Url::parse("tcp+tls://127.0.0.1:18341")?;
- sync_settings.inbound_addrs = vec![bob_url];
- sync_settings.peers = vec![alice_url];
- let bob_consensus_url = Url::parse("tcp+tls://127.0.0.1:18351")?;
- consensus_settings.inbound_addrs = vec![bob_consensus_url];
- consensus_settings.peers = vec![alice_consensus_url];
- let bob = generate_node(
- &vks,
- &validator_config,
- &sync_settings,
- Some(&consensus_settings),
- ex,
- false,
- )
- .await?;
- Ok(Self { config, vks, validator_config, alice, bob })
- }
- pub async fn validate_chains(&self, total_blocks: usize, total_slots: usize) -> Result<()> {
- let genesis_txs_total = self.config.alice_initial + self.config.bob_initial;
- let alice = &self.alice.validator.read().await;
- let bob = &self.bob.validator.read().await;
- alice.validate_blockchain(genesis_txs_total, vec![]).await?;
- bob.validate_blockchain(genesis_txs_total, vec![]).await?;
- let alice_blockchain_len = alice.blockchain.len();
- assert_eq!(alice_blockchain_len, bob.blockchain.len());
- assert_eq!(alice_blockchain_len, total_blocks);
- let alice_slots_len = alice.blockchain.slots.len();
- assert_eq!(alice_slots_len, bob.blockchain.slots.len());
- assert_eq!(alice_slots_len, total_slots);
- Ok(())
- }
- pub async fn add_blocks(&self, blocks: &[BlockInfo]) -> Result<()> {
- // We simply broadcast the block using Alice's sync P2P
- for block in blocks {
- self.alice.sync_p2p.broadcast(block).await;
- }
- // and then add it to her chain
- self.alice.validator.read().await.add_blocks(blocks).await?;
- Ok(())
- }
- pub async fn generate_next_block(
- &self,
- previous: &BlockInfo,
- slots_count: usize,
- ) -> Result<BlockInfo> {
- let previous_hash = previous.blockhash();
- // Generate empty slots
- let mut slots = Vec::with_capacity(slots_count);
- let mut previous_slot = previous.slots.last().unwrap().clone();
- for i in 0..slots_count {
- let id = previous_slot.id + 1;
- // First slot in the sequence has (at least) 1 previous slot producer
- let producers = if i == 0 { 1 } else { 0 };
- let previous = PreviousSlot::new(
- producers,
- vec![previous_hash],
- vec![previous.header.previous.clone()],
- pallas::Base::ZERO,
- previous_slot.pid.error,
- );
- let (f, error, sigma1, sigma2) = slot_pid_output(&previous_slot, producers);
- let pid = PidOutput::new(f, error, sigma1, sigma2);
- let total_tokens = previous_slot.total_tokens + previous_slot.reward;
- // Only last slot in the sequence has a reward
- let reward = if i == slots_count - 1 { next_block_reward() } else { 0 };
- let slot = Slot::new(id, previous, pid, total_tokens, reward);
- slots.push(slot.clone());
- previous_slot = slot;
- }
- // We increment timestamp so we don't have to use sleep
- let mut timestamp = previous.header.timestamp;
- timestamp.add(1);
- // Generate header
- let header = Header::new(
- previous_hash,
- previous.header.epoch,
- slots.last().unwrap().id,
- timestamp,
- previous.header.root.clone(),
- );
- // Generate block
- let block = BlockInfo::new(header, vec![], previous.producer.clone(), slots);
- Ok(block)
- }
- }
- pub async fn generate_node(
- vks: &Vec<(Vec<u8>, String, Vec<u8>)>,
- config: &ValidatorConfig,
- sync_settings: &Settings,
- consensus_settings: Option<&Settings>,
- ex: &Arc<smol::Executor<'_>>,
- skip_sync: bool,
- ) -> Result<Darkfid> {
- let sled_db = sled::Config::new().temporary(true).open()?;
- vks::inject(&sled_db, &vks)?;
- let validator = Validator::new(&sled_db, config.clone()).await?;
- let sync_p2p = spawn_sync_p2p(&sync_settings, &validator).await;
- let consensus_p2p = if let Some(settings) = consensus_settings {
- Some(spawn_consensus_p2p(settings, &validator).await)
- } else {
- None
- };
- let node = Darkfid::new(sync_p2p.clone(), consensus_p2p.clone(), validator).await;
- sync_p2p.clone().start(ex.clone()).await?;
- let _ex = ex.clone();
- ex.spawn(async move {
- if let Err(e) = sync_p2p.run(_ex).await {
- error!("Failed starting sync P2P network: {}", e);
- }
- })
- .detach();
- if consensus_settings.is_some() {
- consensus_p2p.clone().unwrap().start(ex.clone()).await?;
- let _ex = ex.clone();
- ex.spawn(async move {
- if let Err(e) = consensus_p2p.unwrap().run(_ex).await {
- error!("Failed starting consensus P2P network: {}", e);
- }
- })
- .detach();
- }
- if !skip_sync {
- sync_task(&node).await?;
- } else {
- node.validator.write().await.synced = true;
- }
- Ok(node)
- }
|