// Copyright 2018 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. extern crate grin_api as api; extern crate grin_chain as chain; extern crate grin_core as core; extern crate grin_p2p as p2p; extern crate grin_servers as servers; extern crate grin_util as util; extern crate grin_wallet as wallet; mod framework; use std::default::Default; use std::{thread, time}; use core::core::hash::Hashed; use core::global::{self, ChainTypes}; use framework::{config, stratum_config, stop_all_servers, LocalServerContainerConfig, LocalServerContainerPool, LocalServerContainerPoolConfig}; /// Testing the frameworks by starting a fresh server, creating a genesis /// Block and mining into a wallet for a bit #[test] fn basic_genesis_mine() { util::init_test_logger(); global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "genesis_mine"; framework::clean_all_output(test_name_dir); // Create a server pool let mut pool_config = LocalServerContainerPoolConfig::default(); pool_config.base_name = String::from(test_name_dir); pool_config.run_length_in_seconds = 10; pool_config.base_api_port = 30000; pool_config.base_p2p_port = 31000; pool_config.base_wallet_port = 32000; let mut pool = LocalServerContainerPool::new(pool_config); // Create a server to add into the pool let mut server_config = LocalServerContainerConfig::default(); server_config.start_miner = true; server_config.start_wallet = false; server_config.burn_mining_rewards = true; pool.create_server(&mut server_config); let servers = pool.run_all_servers(); stop_all_servers(servers); } /// Creates 5 servers, first being a seed and check that through peer address /// messages they all end up connected. #[test] fn simulate_seeding() { util::init_test_logger(); global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "simulate_seeding"; framework::clean_all_output(test_name_dir); // Create a server pool let mut pool_config = LocalServerContainerPoolConfig::default(); pool_config.base_name = String::from(test_name_dir); pool_config.run_length_in_seconds = 30; // have to use different ports because of tests being run in parallel pool_config.base_api_port = 30020; pool_config.base_p2p_port = 31020; pool_config.base_wallet_port = 32020; let mut pool = LocalServerContainerPool::new(pool_config); // Create a first seed server to add into the pool let mut server_config = LocalServerContainerConfig::default(); // server_config.start_miner = true; server_config.start_wallet = false; server_config.burn_mining_rewards = true; server_config.is_seeding = true; pool.create_server(&mut server_config); // point next servers at first seed server_config.is_seeding = false; server_config.seed_addr = String::from(format!( "{}:{}", server_config.base_addr, server_config.p2p_server_port )); for _ in 0..4 { pool.create_server(&mut server_config); } // pool.connect_all_peers(); let servers = pool.run_all_servers(); stop_all_servers(servers); } /// Create 1 server, start it mining, then connect 4 other peers mining and /// using the first /// as a seed. Meant to test the evolution of mining difficulty with miners /// running at /// different rates // Just going to comment this out as an automatically run test for the time // being, // As it's more for actively testing and hurts CI a lot //#[test] #[allow(dead_code)] fn simulate_parallel_mining() { global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "simulate_parallel_mining"; // framework::clean_all_output(test_name_dir); // Create a server pool let mut pool_config = LocalServerContainerPoolConfig::default(); pool_config.base_name = String::from(test_name_dir); pool_config.run_length_in_seconds = 60; // have to use different ports because of tests being run in parallel pool_config.base_api_port = 30040; pool_config.base_p2p_port = 31040; pool_config.base_wallet_port = 32040; let mut pool = LocalServerContainerPool::new(pool_config); // Create a first seed server to add into the pool let mut server_config = LocalServerContainerConfig::default(); server_config.start_miner = true; server_config.start_wallet = true; server_config.is_seeding = true; pool.create_server(&mut server_config); // point next servers at first seed server_config.is_seeding = false; server_config.seed_addr = String::from(format!( "{}:{}", server_config.base_addr, server_config.p2p_server_port )); // And create 4 more, then let them run for a while for i in 1..4 { // fudge in some slowdown server_config.miner_slowdown_in_millis = i * 2; pool.create_server(&mut server_config); } // pool.connect_all_peers(); let servers = pool.run_all_servers(); stop_all_servers(servers); // Check mining difficulty here?, though I'd think it's more valuable // to simply output it. Can at least see the evolution of the difficulty target // in the debug log output for now } // TODO: Convert these tests to newer framework format /// Create a network of 5 servers and mine a block, verifying that the block /// gets propagated to all. #[test] fn simulate_block_propagation() { util::init_test_logger(); // we actually set the chain_type in the ServerConfig below // TODO - avoid needing to set it in two places? global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "grin-prop"; framework::clean_all_output(test_name_dir); // instantiates 5 servers on different ports let mut servers = vec![]; for n in 0..5 { let s = servers::Server::new(framework::config(10 * n, test_name_dir, 0)).unwrap(); servers.push(s); thread::sleep(time::Duration::from_millis(100)); } // start mining servers[0].start_test_miner(None); // monitor for a change of head on a different server and check whether // chain height has changed loop { let mut count = 0; for n in 0..5 { if servers[n].head().height > 3 { count += 1; } } if count == 5 { break; } thread::sleep(time::Duration::from_millis(1_000)); } for n in 0..5 { servers[n].stop(); } } /// Creates 2 different disconnected servers, mine a few blocks on one, connect /// them and check that the 2nd gets all the blocks #[test] fn simulate_full_sync() { util::init_test_logger(); // we actually set the chain_type in the ServerConfig below global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "grin-sync"; framework::clean_all_output(test_name_dir); let s1 = servers::Server::new(framework::config(1000, "grin-sync", 1000)).unwrap(); // mine a few blocks on server 1 s1.start_test_miner(None); thread::sleep(time::Duration::from_secs(8)); let s2 = servers::Server::new(framework::config(1001, "grin-sync", 1000)).unwrap(); // Get the current header from s1. let s1_header = s1.chain.head_header().unwrap(); // Wait for s2 to sync up to and including the header from s1. while s2.head().height < s1_header.height { thread::sleep(time::Duration::from_millis(1_000)); } // Confirm both s1 and s2 see a consistent header at that height. let s2_header = s2.chain.get_block_header(&s1_header.hash()).unwrap(); assert_eq!(s1_header, s2_header); // Stop our servers cleanly. s1.stop(); s2.stop(); } /// Creates 2 different disconnected servers, mine a few blocks on one, connect /// them and check that the 2nd gets all using fast sync algo #[test] fn simulate_fast_sync() { util::init_test_logger(); // we actually set the chain_type in the ServerConfig below global::set_mining_mode(ChainTypes::AutomatedTesting); let test_name_dir = "grin-fast"; framework::clean_all_output(test_name_dir); // start s1 and mine enough blocks to get beyond the fast sync horizon let s1 = servers::Server::new(framework::config(2000, "grin-fast", 2000)).unwrap(); s1.start_test_miner(None); while s1.head().height < 20 { thread::sleep(time::Duration::from_millis(1_000)); } let mut conf = config(2001, "grin-fast", 2000); conf.archive_mode = Some(false); let s2 = servers::Server::new(conf).unwrap(); while s2.header_head().height < 1 { s2.ping_peers(); thread::sleep(time::Duration::from_millis(1_000)); } s1.stop_test_miner(); // Get the current header from s1. let s1_header = s1.chain.head_header().unwrap(); // Wait for s2 to sync up to and including the header from s1. while s2.head().height < s1_header.height { thread::sleep(time::Duration::from_millis(1_000)); } // Confirm both s1 and s2 see a consistent header at that height. let s2_header = s2.chain.get_block_header(&s1_header.hash()).unwrap(); assert_eq!(s1_header, s2_header); // Stop our servers cleanly. s1.stop(); s2.stop(); } // #[test] fn simulate_fast_sync_double() { util::init_test_logger(); // we actually set the chain_type in the ServerConfig below global::set_mining_mode(ChainTypes::AutomatedTesting); framework::clean_all_output("grin-double-fast1"); framework::clean_all_output("grin-double-fast2"); let s1 = servers::Server::new(framework::config(3000, "grin-double-fast1", 3000)).unwrap(); // mine a few blocks on server 1 s1.start_test_miner(None); thread::sleep(time::Duration::from_secs(8)); { let mut conf = config(3001, "grin-double-fast2", 3000); conf.archive_mode = Some(false); let s2 = servers::Server::new(conf).unwrap(); while s2.head().height != s2.header_head().height || s2.head().height < 20 { thread::sleep(time::Duration::from_millis(1000)); } s2.stop(); } // locks files don't seem to be cleaned properly until process exit std::fs::remove_file("target/tmp/grin-double-fast2/grin-sync-1001/chain/LOCK").unwrap(); std::fs::remove_file("target/tmp/grin-double-fast2/grin-sync-1001/peers/LOCK").unwrap(); thread::sleep(time::Duration::from_secs(20)); let mut conf = config(3001, "grin-double-fast2", 3000); conf.archive_mode = Some(false); let s2 = servers::Server::new(conf).unwrap(); while s2.head().height != s2.header_head().height || s2.head().height < 50 { thread::sleep(time::Duration::from_millis(1000)); } s1.stop(); s2.stop(); }