diff --git a/Cargo.lock b/Cargo.lock index f40c81f10369de38b94ad0e9a67c7be7d4b1f212..b727cd5f2799ffd74bc980811a3f4fac2c8489dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -930,6 +930,7 @@ dependencies = [ "dup-crypto-tests-tools 0.1.0", "durs-bc-db-reader 0.3.0-dev", "durs-bc-db-writer 0.3.0-dev", + "durs-common-tests-tools 0.1.0", "durs-common-tools 0.2.0", "durs-conf 0.3.0-dev", "durs-message 0.3.0-dev", @@ -942,6 +943,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/lib/modules/blockchain/blockchain/tests/common.rs b/lib/modules/blockchain/blockchain/tests/common.rs new file mode 100644 index 0000000000000000000000000000000000000000..d87405b5d93ea8eb736d234e3a323975d7d29654 --- /dev/null +++ b/lib/modules/blockchain/blockchain/tests/common.rs @@ -0,0 +1,77 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// 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 dubp_currency_params::genesis_block_params::v10::BlockV10Parameters; +use dubp_currency_params::{CurrencyName, CurrencyParameters}; +use durs_bc_db_writer::WotsV10DBs; +use durs_blockchain::BlockchainModule; +use durs_message::DursMsg; +use durs_module::RouterThreadMessage; +use std::path::{Path, PathBuf}; +use std::sync::mpsc::Sender; +use std::thread::JoinHandle; +use tempfile::TempDir; + +pub static TEST_CURRENCY: &str = "test_currency"; + +/// Init logger and user datas directory +pub fn init() -> PathBuf { + durs_common_tests_tools::logger::init_logger_stdout(vec![]); + TempDir::new().expect("Fail to create tmp dir.").into_path() +} + +/// Stop and clear test +pub fn stop_and_clean( + bc_sender: Sender<DursMsg>, + handle: JoinHandle<()>, + tmp_profile_path: PathBuf, +) { + // Send STOP signal to blockchain module + bc_sender + .send(DursMsg::Stop) + .expect("Fail to send stop signal to blockchain module."); + handle + .join() + .expect("Blockchain module fail to stop correctly."); + + // Clear user datas + std::fs::remove_dir_all(tmp_profile_path).expect("Fail to remove tmp dir."); +} + +/// Initialize a BlockchainModule with empty blockchain +pub fn init_bc_module( + fake_router_sender: Sender<RouterThreadMessage<DursMsg>>, + genesis_block_parameters: BlockV10Parameters, + tmp_path: &Path, +) -> BlockchainModule { + let currency_name = CurrencyName(TEST_CURRENCY.to_owned()); + //let profile_path = tmp_profile_path.to_owned(); + + //let dbs_path = durs_conf::get_blockchain_db_path(profile_path.clone()); + let db = durs_bc_db_writer::open_db(tmp_path).expect("Fail to open blockchain DB."); + + BlockchainModule::new( + fake_router_sender, + tmp_path.to_owned(), + Some(currency_name.clone()), + Some(CurrencyParameters::from(( + ¤cy_name, + genesis_block_parameters, + ))), + db, + WotsV10DBs::open(None), + ) + .expect("Fail to init BlockchainModule with empty blockchain.") +} diff --git a/lib/modules/blockchain/blockchain/tests/revert_blocks.rs b/lib/modules/blockchain/blockchain/tests/revert_blocks.rs new file mode 100644 index 0000000000000000000000000000000000000000..2e5a77fd383d4516b5340b3b81372e3bfe100d5b --- /dev/null +++ b/lib/modules/blockchain/blockchain/tests/revert_blocks.rs @@ -0,0 +1,162 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// 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/>. + +mod common; + +use crate::common::*; +use dubp_block_doc::{block::BlockDocumentTrait, BlockDocument}; +use dubp_common_doc::BlockNumber; +use dubp_currency_params::genesis_block_params::v10::BlockV10Parameters; +use dup_crypto::keys::{KeyPair, Signator, SignatorEnum}; +use durs_blockchain::BlockchainModule; +use durs_message::events::{BlockchainEvent, DursEvent}; +use durs_message::requests::DursReqContent; +use durs_message::DursMsg; +use durs_module::{ + ModuleEvent, ModuleReqFullId, ModuleReqId, ModuleRole, ModuleStaticName, RouterThreadMessage, +}; +use durs_network::events::NetworkEvent; +use durs_network::requests::OldNetworkRequest; +use pretty_assertions::assert_eq; +use std::sync::mpsc::{channel, Receiver, Sender}; + +#[test] +fn test_revert_block() { + // Init test + let tmp_profile_path = common::init(); + + // Router channel + let (router_sender, router_receiver) = channel(); // RouterThreadMessage<DursMsg> + + let genesis_params = BlockV10Parameters::default(); + + let mut bc = init_bc_module(router_sender, genesis_params, tmp_profile_path.as_path()); + + // Create blockchain module channel + let (bc_sender, bc_receiver): (Sender<DursMsg>, Receiver<DursMsg>) = channel(); + + let handle = std::thread::spawn(move || { + bc.start_blockchain(&bc_receiver, None); + }); + + // Receive 11 requests GetBlocks + for i in 0..11 { + let msg = router_receiver + .recv() + .expect("blockchain module disconnected."); + if let RouterThreadMessage::ModuleMessage(durs_msg) = msg { + assert_eq!( + DursMsg::Request { + req_from: BlockchainModule::name(), + req_to: ModuleRole::InterNodesNetwork, + req_id: ModuleReqId(i), + req_content: DursReqContent::OldNetworkRequest(OldNetworkRequest::GetBlocks( + ModuleReqFullId(BlockchainModule::name(), ModuleReqId(i)), + 50, + i * 50 + )), + }, + durs_msg + ); + log::info!("Router receive: {:?}", durs_msg); + } else { + panic!("Expect ModuleMesage, found: {:?}", msg) + } + } + + // Receive first g1-test chunk + let gt_chunk_0 = dubp_blocks_tests_tools::gt::get_gt_chunk(0); + receive_valid_blocks(&bc_sender, &router_receiver, gt_chunk_0); + + // Receive second g1-test chunk + let gt_chunk_1 = dubp_blocks_tests_tools::gt::get_gt_chunk(1); + receive_valid_blocks(&bc_sender, &router_receiver, gt_chunk_1); + + // Receive third g1-test chunk + let mut gt_chunk_2 = dubp_blocks_tests_tools::gt::get_gt_chunk(2); + gt_chunk_2.truncate(50); + let block_546 = gt_chunk_2.get(46).cloned().expect("gt_chunk_2 is empty !"); + receive_valid_blocks(&bc_sender, &router_receiver, gt_chunk_2); + + // Generate 6 forks blocks from 547 to 553 + let signator = SignatorEnum::Ed25519( + dup_crypto::keys::ed25519::Ed25519KeyPair::generate_random() + .generate_signator() + .expect("fail to generatye signator"), + ); + let mut fork_blocks = Vec::new(); + let mut previous_hash = block_546.hash().expect("block_546 have None hash").0; + let mut bc_time = block_546.common_time(); + for n in 547..=553 { + bc_time += 301; + let block = dubp_blocks_tests_tools::mocks::gen_empty_timed_issued_hashed_block_v10( + BlockNumber(n), + bc_time, + signator.public_key(), + previous_hash, + &signator, + ); + let block_hash = block.hash().clone().expect("block must have hash"); + fork_blocks.push(BlockDocument::V10(block)); + previous_hash = block_hash.0; + } + + // Cause the revert of 3 blocks (send forks blocks from 547) + receive_valid_blocks(&bc_sender, &router_receiver, fork_blocks); + + // TODO verify that we have switched to the new branch + + // let msg2 = router_receiver + // .recv() + // .expect("blockchain module disconnected."); + // log::info!("Router receive: {:?}", msg2); + + // Stop and clean + common::stop_and_clean(bc_sender, handle, tmp_profile_path); +} + +fn receive_valid_blocks( + bc_sender: &Sender<DursMsg>, + router_receiver: &Receiver<RouterThreadMessage<DursMsg>>, + blocks: Vec<BlockDocument>, +) { + bc_sender + .send(DursMsg::Event { + event_from: ModuleStaticName("toto"), + event_type: ModuleEvent::NewBlockFromNetwork, + event_content: DursEvent::NetworkEvent(NetworkEvent::ReceiveBlocks(blocks.clone())), + }) + .expect("Fail to send blocks to blockchain module."); + for block in blocks { + let msg = router_receiver + .recv() + .expect("blockchain module disconnected."); + if let RouterThreadMessage::ModuleMessage(durs_msg) = msg { + assert_eq!( + DursMsg::Event { + event_from: ModuleStaticName("blockchain"), + event_type: ModuleEvent::NewValidBlock, + event_content: DursEvent::BlockchainEvent(Box::new( + BlockchainEvent::StackUpValidBlock(Box::new(block)) + )), + }, + durs_msg + ); + //log::debug!("Router receive: {:?}", msg); + } else { + panic!("Expect ModuleMesage, found: {:?}", msg) + } + } +}