diff --git a/core/src/consensus.rs b/core/src/consensus.rs index d0c35b1a9..a7a474e0e 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -60,6 +60,10 @@ pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32); /// peer-to-peer networking layer only for DoS protection. pub const MAX_MSG_LEN: u64 = 20_000_000; +/// The minimum mining difficulty we'll allow + +pub const MINIMUM_DIFFICULTY: u32 = 10; + pub const MEDIAN_TIME_WINDOW: u32 = 11; pub const DIFFICULTY_ADJUST_WINDOW: u32 = 23; @@ -126,7 +130,7 @@ pub fn next_difficulty(cursor: T) -> Result // Check we have enough blocks if window_end.len() < (MEDIAN_TIME_WINDOW as usize) { - return Ok(Difficulty::one()); + return Ok(Difficulty::from_num(MINIMUM_DIFFICULTY)); } // Calculating time medians at the beginning and end of the window. @@ -136,7 +140,7 @@ pub fn next_difficulty(cursor: T) -> Result let end_ts = window_end[window_end.len() / 2]; // Average difficulty and dampened average time - let diff_avg = diff_sum / Difficulty::from_num(DIFFICULTY_ADJUST_WINDOW); + let diff_avg = diff_sum.clone() / Difficulty::from_num(DIFFICULTY_ADJUST_WINDOW); let ts_damp = (3 * BLOCK_TIME_WINDOW + (begin_ts - end_ts)) / 4; // Apply time bounds @@ -148,7 +152,6 @@ pub fn next_difficulty(cursor: T) -> Result ts_damp }; - // Final ratio calculation Ok(diff_avg * Difficulty::from_num(BLOCK_TIME_WINDOW as u32) / Difficulty::from_num(adj_ts as u32)) } @@ -185,13 +188,16 @@ mod test { #[test] fn next_target_adjustment() { // not enough data - assert_eq!(next_difficulty(vec![]).unwrap(), Difficulty::one()); + assert_eq!(next_difficulty(vec![]).unwrap(), Difficulty::from_num(MINIMUM_DIFFICULTY)); + assert_eq!(next_difficulty(vec![Ok((60, Difficulty::one()))]).unwrap(), - Difficulty::one()); + Difficulty::from_num(MINIMUM_DIFFICULTY)); + assert_eq!(next_difficulty(repeat(60, 10, DIFFICULTY_ADJUST_WINDOW)).unwrap(), - Difficulty::one()); + Difficulty::from_num(MINIMUM_DIFFICULTY)); // just enough data, right interval, should stay constant + let just_enough = DIFFICULTY_ADJUST_WINDOW + MEDIAN_TIME_WINDOW; assert_eq!(next_difficulty(repeat(60, 1000, just_enough)).unwrap(), Difficulty::from_num(1000)); diff --git a/core/src/core/block.rs b/core/src/core/block.rs index d954a8a88..4e052ca58 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -23,6 +23,7 @@ use core::Committed; use core::{Input, Output, Proof, TxKernel, Transaction, COINBASE_KERNEL, COINBASE_OUTPUT}; use core::transaction::merkle_inputs_outputs; use consensus::REWARD; +use consensus::MINIMUM_DIFFICULTY; use core::hash::{Hash, Hashed, ZERO_HASH}; use core::target::Difficulty; use ser::{self, Readable, Reader, Writeable, Writer}; @@ -65,8 +66,8 @@ impl Default for BlockHeader { height: 0, previous: ZERO_HASH, timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }), - difficulty: Difficulty::one(), - total_difficulty: Difficulty::one(), + difficulty: Difficulty::from_num(MINIMUM_DIFFICULTY), + total_difficulty: Difficulty::from_num(MINIMUM_DIFFICULTY), utxo_merkle: ZERO_HASH, tx_merkle: ZERO_HASH, features: DEFAULT_BLOCK, diff --git a/core/src/core/target.rs b/core/src/core/target.rs index bbbb2b6d6..4113c3d2a 100644 --- a/core/src/core/target.rs +++ b/core/src/core/target.rs @@ -17,7 +17,7 @@ //! the related difficulty, defined as the maximum target divided by the hash. use std::fmt; -use std::ops::{Add, Mul, Div}; +use std::ops::{Add, Mul, Div, Sub}; use bigint::BigUint; use serde::{Serialize, Serializer, Deserialize, Deserializer, de}; @@ -86,6 +86,13 @@ impl Add for Difficulty { } } +impl Sub for Difficulty { + type Output = Difficulty; + fn sub(self, other: Difficulty) -> Difficulty { + Difficulty { num: self.num - other.num } + } +} + impl Mul for Difficulty { type Output = Difficulty; fn mul(self, other: Difficulty) -> Difficulty { diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 60fca856a..cd693507f 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -18,6 +18,7 @@ use time; use core; use consensus::DEFAULT_SIZESHIFT; +use consensus::MINIMUM_DIFFICULTY; use core::hash::Hashed; use core::target::Difficulty; @@ -34,8 +35,8 @@ pub fn genesis() -> core::Block { tm_mday: 4, ..time::empty_tm() }, - difficulty: Difficulty::one(), - total_difficulty: Difficulty::one(), + difficulty: Difficulty::from_num(MINIMUM_DIFFICULTY), + total_difficulty: Difficulty::from_num(MINIMUM_DIFFICULTY), utxo_merkle: [].hash(), tx_merkle: [].hash(), features: core::DEFAULT_BLOCK, diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 51687fe2e..d79976cce 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -28,6 +28,7 @@ pub mod cuckoo; use time; use consensus::EASINESS; +use consensus::MINIMUM_DIFFICULTY; use core::BlockHeader; use core::hash::Hashed; use core::target::Difficulty; @@ -92,9 +93,9 @@ mod test { fn genesis_pow() { let mut b = genesis::genesis(); b.header.nonce = 310; - pow_size(&mut b.header, Difficulty::one(), 12).unwrap(); + pow_size(&mut b.header, Difficulty::from_num(MINIMUM_DIFFICULTY), 12).unwrap(); assert!(b.header.nonce != 310); - assert!(b.header.pow.to_difficulty() >= Difficulty::one()); + assert!(b.header.pow.to_difficulty() >= Difficulty::from_num(MINIMUM_DIFFICULTY)); assert!(verify_size(&b.header, 12)); } } diff --git a/doc/pow/images/cuckoo_base.png b/doc/pow/images/cuckoo_base.png new file mode 100644 index 000000000..e74954330 Binary files /dev/null and b/doc/pow/images/cuckoo_base.png differ diff --git a/doc/pow/images/cuckoo_base_numbered.png b/doc/pow/images/cuckoo_base_numbered.png new file mode 100644 index 000000000..e74954330 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered.png differ diff --git a/doc/pow/images/cuckoo_base_numbered_few_edges.png b/doc/pow/images/cuckoo_base_numbered_few_edges.png new file mode 100644 index 000000000..d1b9c3068 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered_few_edges.png differ diff --git a/doc/pow/images/cuckoo_base_numbered_few_edges_cycle.png b/doc/pow/images/cuckoo_base_numbered_few_edges_cycle.png new file mode 100644 index 000000000..b21eb48f1 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered_few_edges_cycle.png differ diff --git a/doc/pow/images/cuckoo_base_numbered_many.png b/doc/pow/images/cuckoo_base_numbered_many.png new file mode 100644 index 000000000..8010673d5 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered_many.png differ diff --git a/doc/pow/images/cuckoo_base_numbered_many_edges.png b/doc/pow/images/cuckoo_base_numbered_many_edges.png new file mode 100644 index 000000000..7569ef649 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered_many_edges.png differ diff --git a/doc/pow/images/cuckoo_base_numbered_minimal.png b/doc/pow/images/cuckoo_base_numbered_minimal.png new file mode 100644 index 000000000..aa1394656 Binary files /dev/null and b/doc/pow/images/cuckoo_base_numbered_minimal.png differ diff --git a/doc/pow/pow.md b/doc/pow/pow.md new file mode 100644 index 000000000..9200422a8 --- /dev/null +++ b/doc/pow/pow.md @@ -0,0 +1,212 @@ +Grin's Proof-of-Work +==================== + +[WIP and subject to review, may still contain errors] + +This document is meant to outline, at a level suitable for someone without prior knowledge, +the algorithms and processes currently involved in Grin's Proof-of-Work system. We'll start +with a general overview of cycles in a graph and the Cuckoo Cycle algorithm which forms the +basis of Grin's proof-of-work. We'll then move on to Grin-specific details, which will outline +the other systems that combine with Cuckoo Cycles to form the entirety of mining in Grin. + +Please note that Grin is currently under active development, and any and all of this is subject to +(and will) change before a general release. + +# Graphs and Cuckoo Cycles + +Grin's basic Proof-of-Work algorithm is called Cuckoo Cycle, which is specifically designed +to be resistant to Bitcoin style hardware arms-races. It is primarily a memory bound algorithm, +which, (at least in theory,) means that solution time is limited to the speed of a system's RAM +rather than processor or GPU speed. As such, mining Cuckoo Cycle solutions should be viable on +most commodity hardware, and require far less energy than most other GPU, CPU or ASIC-bound +proof of work algorithms. + +The Cuckoo Cycle POW is the work of John Tromp, and the most up-to-date documentation and implementations +can be found in [his github repository](https://github.com/tromp/cuckoo). The +[white paper](https://github.com/tromp/cuckoo/blob/master/doc/cuckoo.pdf) is the best source of +further technical details. + +## Cycles in a Graph + +Cuckoo Cycles is an algorithm meant to detect cycles in a random bipartite graphs graph of N nodes and M edges. +In plainer terms, a Node is simply an element storing a value, an Edge is a line connecting two nodes, +and a graph is bipartite when it's split into two groupings. The simple +graph below, with values placed at random, denotes just such a graph, with 8 Nodes storing 8 values +divided into 2 groups (one row on top and one row on the bottom,) and zero Edges (i.e. no lines +connecting any nodes.) + +![alt text](images/cuckoo_base_numbered_minimal.png) + +*A graph of 8 Nodes with Zero Edges* + +Let's throw a few Edges into the graph now, randomly: + +![alt text](images/cuckoo_base_numbered_few_edges.png) + +*8 Nodes with 4 Edges* + +We now have a randomly-generated graph with 8 nodes (N) and 4 edges (M), or an NxM graph where +N=8 and M=4. Our basic Proof-of-Work is now concerned with finding 'cycles' of a certain length +within this random graph, or, put simply, a path of connected nodes. So, if we were looking +for a cycle of length 3 (a path connecting 3 nodes), one can be detected in this graph, +i.e. the path running from 5 to 6 to 3: + +![alt text](images/cuckoo_base_numbered_few_edges_cycle.png) + +*Cycle found* + +Adjusting the number of Edges M relative to the number of Nodes N changes the difficulty of the +cycle-finding problem, and the probability that a cycle exists in the current graph. For instance, +if our POW problem were to find a cycle of length 5 in the graph, the current difficulty of 5/8 (M/N) +would mean that all 4 edges would need to be randomly generated in a perfect cycle in order for +there to be a solution. If you increase the number of edges relative to the number of nodes, +you increase the probability that a solution exists: + +![alt text](images/cuckoo_base_numbered_many_edges.png) + +*MxN = 9x8 - Cycle of length 5 found* + +So modifying the ratio M/N changes the number of expected occurrences of a cycle within a randomly +generated graph. + +For a small graph such as the one above, determining whether a cycle of a certain length exists is trivial. +But as the graphs get larger, detecting such cycles becomes more difficult. For instance, does this + graph have a cycle of length 7, i.e. 7 directly connected nodes? + +![alt text](images/cuckoo_base_numbered_many.png) + +*Meat-space Cycle Detection exercise* + +The answer is left as an exercise to the reader, but the overall takeaway is that detecting such cycles becomes +a more difficult exercise as the size of a graph grows. It also becomes easier as M/N becomes larger, i.e. you add more edges relative to the number of nodes in a graph. + +## Cuckoo Cycles + +The Cuckoo Cycles algorithm is a specialised algorithm designed to solve exactly this problem, and it does +so by inserting values into a structure called a 'Cuckoo Hashtable' according to a hash which maps nodes +into possible locations in two separate arrays. This document won't go into detail on the base algorithm, as +it's outlined in plain enough detail in section 5 of the +[white paper](https://github.com/tromp/cuckoo/blob/master/doc/cuckoo.pdf). There are also several +variants on the algorithm that make various speed/memory tradeoffs, again beyond the scope of this document. +However, there are a few details following from the above that we need to keep in mind before going on to more +technical details of Grin's proof-of-work. + +* The 'random' graphs, as detailed above, are not actually random but are generated by putting nodes through a +seeded hashing function, SIPHASH, generating two potential locations (one in each array) for each node in the graph. +The seed will come from a hash of a block header, outlined further below. +* The 'Proof' created by this algorithm is a set of nonces that generate the cycle, which can be trivially validated by other nodes. +* Two main parameters, as explained above, are passed into the Cuckoo Cycle algorithm that affect the probability of a solution, and the +time it takes to search the graph for a solution: + * The M/N ratio outlined above, which controls the number of edges relative to the size of the graph + * The size of the graph itself + +How these parameters interact in practice is looked at in more [detail below](#mining-loop-difficulty-control-and-timing). + +Now, (hopefully) armed with a basic understanding of what the Cuckoo Cycle algorithm is intended to do, as well as the parameters that affect how difficult it is to find a solution, we move on to the other portions of Grin's POW system. + +# Mining in Grin + +The Cuckoo Cycle outlined above forms the basis of Grin's mining process, however Grin uses Cuckoo Cycles in tandem with several other systems to create a Proof-of-Work. + +### Additional Difficulty Control + +In order to provide additional difficulty control in a manner that meets the needs of a network with constantly evolving hashpower +availability, a further Hashcash-based difficulty check is applied to potential solution sets as follows: + +If the SHA256 hash +of a potential set of solution nonces (currently an array of 42 u32s representing the cycle nonces,) +is less than an evolving difficulty target T, then the solution is considered valid. More precisely, +the proof difficulty is calculated as the maximum target hash (2^256) divided by the current hash, +rounded to give an integer. If this integer is larger than the evolving network difficulty, the POW +is considered valid and the block is submit to the chain for validation. + +In other words, a potential proof, as well as containting a valid Cuckoo Cycle, also needs to hash to a value higher than the target difficulty. This difficulty is derived from: + +### Evolving Network Difficulty + +The difficulty target is intended to evolve according to the available network hashpower, with the goal of +keeping the average block solution time within range of a target (currently 60 seconds, though this is subject +to change). + +The difficulty calculation is based on both Digishield and GravityWave family of difficulty computation, +coming to something very close to Zcash. The refence difficulty is an average of the difficulty over a window of +23 blocks (the current consensus value). The corresponding timespan is calculated by using the difference between +the median timestamps at the beginning and the end of the window. If the timespan is higher or lower than a certain +range, (adjusted with a dampening factor to allow for normal variation,) then the difficulty is raised or lowered +to a value aiming for the target block solve time. + +### The Mining Loop + +All of these systems are put together in the mining loop, which attempts to create +valid Proofs-of-Work to create the latest block in the chain. The following is an outline of what the main mining loop does during a single iteration: + +* Get the latest chain state and build a block on top of it, which includes + * A Block Header with new values particular to this mining attempt, which are: + + * The latest target difficulty as selected by the [evolving network difficulty](#evolving-network-difficulty) algorithm + * A set of transactions available for validation selected from the transaction pool + * A coinbase transaction (which we're hoping to give to ourselves) + * The current timestamp + * A randomly generated nonce to add further randomness to the header's hash + * The merkle root of the UTXO set and fees (not yet implemented) + + * Then, a sub-loop runs for a set amount of time, currently configured at 2 seconds, where the following happens: + + * The new block header is hashed to create a hash value + * The cuckoo graph generator is initialised, which accepts as parameters: + * The hash of the potential block header, which is to be used as the key to a SIPHASH function + that will generate pairs of locations for each node in the graph. + * The size of the graph (a consensus value). + * An easiness value, (a consensus value) representing the M/N ratio described above denoting the probability + of a solution appearing in the graph + * The Cuckoo Cycle detection algorithm tries to find a solution (i.e. a cycle of length 42) within the generated + graph. + * If a cycle is found, a SHA256 hash of the proof is created and is compared to the current target + difficulty, as outlined in [Additional Difficulty Control](#additional-difficulty-control) above. + * If the SHA256 Hash difficulty is greater than or equal to the target difficulty, the block is sent to the + transaction pool, propogated amongst peers for validation, and work begins on the next block. + * If the SHA256 Hash difficulty is less than the target difficulty, the proof is thrown out and the timed loop continues. + * If no solution is found, increment the nonce in the header by 1, and update the header's timestamp so the next iteration + hashes a different value for seeding the next loop's graph generation step. + * If the loop times out with no solution found, start over again from the top, collecting new transactions and creating + a new block altogether. + +### Mining Loop Difficulty Control and Timing + +Controlling the overall difficulty of the mining loop requires finding a balance between the three values outlined above: + +* Graph size (currently represented as a bit-shift value n representing a size of 2^n nodes, consensus value + DEFAULT_SIZESHIFT). Smaller graphs can be exhaustively searched more quickly, but will also have fewer + solutions for a given easiness value. A very small graph needs a higher easiness value to have the same + chance to have a solution as a larger graph with a lower easiness value. +* The 'Easiness' consensus value, or the M/N ratio of the graph expressed as a percentage. The higher this value, the more likely + it is a generated graph will contain a solution. In tandem with the above, the larger the graph, the more solutions + it will contain for a given easiness value. +* The evolving network difficulty hash. + +These values need to be carefully tweaked in order for the mining algorithm to find the right balance between the +cuckoo graph size and the evolving difficulty. The POW needs to remain mostly Cuckoo Cycle based, but still allow for +reasonably short block times that allow new transactions to be quickly processed. + +If the graph size is too low and the easiness too high, for instance, then many cuckoo cycle solutions can easily be +found for a given block, and the POW will start to favour those who can hash faster, precisely what Cuckoo Cycles is +trying to avoid. If the graph is too large and easiness too low, however, then it can potentially take any solver a +long time to find a solution in a single graph, well outside a window in which you'd like to stop to collect new +transactions. + +These values are currently set to 2^12 for the graph size and 50% for the easiness value, however they are only +temporary values for testing. The current miner implementation is very unoptimised, and the graph size will need +to be changed as faster and more optimised Cuckoo Cycle algorithms are put in place. + +### Pooling Capability + +[More detail needed here] Note that contrary to some concerns about the ability to effectively pool Cuckoo Cycle mining, pooling Grin's POW +as outlined above is relatively straightforward. Members of the pool are able to prove they're working on a solution +by submitting valid proofs that simply fall under the current network target difficulty. + + + + + + + diff --git a/grin/src/miner.rs b/grin/src/miner.rs index 102f03421..5c9e09985 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -24,7 +24,9 @@ use time; use adapters::{ChainToPoolAndNetAdapter, PoolToChainAdapter}; use api; use core::consensus; +use core::consensus::*; use core::core; +use core::core::target::*; use core::core::hash::{Hash, Hashed}; use core::pow::cuckoo; use core::ser; @@ -45,6 +47,10 @@ pub struct Miner { /// chain adapter to net chain_adapter: Arc, tx_pool: Arc>>, + + //Just to hold the port we're on, so this miner can be identified + //while watching debug output + debug_output_id: String, } impl Miner { @@ -62,13 +68,23 @@ impl Miner { chain_store: chain_store, chain_adapter: chain_adapter, tx_pool: tx_pool, + debug_output_id: String::from("none"), } } + /// Keeping this optional so setting in a separate funciton + /// instead of in the new function + + pub fn set_debug_output_id(&mut self, debug_output_id: String){ + self.debug_output_id=debug_output_id; + } + + /// Starts the mining loop, building a new block on top of the existing /// chain anytime required and looking for PoW solution. pub fn run_loop(&self) { - info!("Starting miner loop."); + + info!("(Server ID: {}) Starting miner loop.", self.debug_output_id); let mut coinbase = self.get_coinbase(); loop { // get the latest chain state and build a block on top of it @@ -84,21 +100,29 @@ impl Miner { // transactions) and as long as the head hasn't changed let deadline = time::get_time().sec + 2; let mut sol = None; - debug!("Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.", + debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.", + self.debug_output_id, self.config.cuckoo_size, latest_hash, b.header.difficulty); let mut iter_count = 0; if self.config.slow_down_in_millis > 0 { - debug!("Artifically slowing down loop by {}ms per iteration.", - self.config.slow_down_in_millis); + debug!("(Server ID: {}) Artificially slowing down loop by {}ms per iteration.", + self.debug_output_id, + self.config.slow_down_in_millis); } while head.hash() == latest_hash && time::get_time().sec < deadline { let pow_hash = b.hash(); let mut miner = cuckoo::Miner::new(&pow_hash[..], consensus::EASINESS, self.config.cuckoo_size); if let Ok(proof) = miner.mine() { - if proof.to_difficulty() >= b.header.difficulty { + let proof_diff=proof.to_difficulty(); + debug!("(Server ID: {}) Header difficulty is: {}, Proof difficulty is: {}", + self.debug_output_id, + b.header.difficulty, + proof_diff); + + if proof_diff >= b.header.difficulty { sol = Some(proof); break; } @@ -109,16 +133,17 @@ impl Miner { } iter_count += 1; - //Artifical slow down + //Artificial slow down if self.config.slow_down_in_millis > 0 { - thread::sleep(std::time::Duration::from_millis(2000)); + thread::sleep(std::time::Duration::from_millis(self.config.slow_down_in_millis)); } } // if we found a solution, push our block out if let Some(proof) = sol { - info!("Found valid proof of work, adding block {}.", b.hash()); - b.header.pow = proof; + info!("(Server ID: {}) Found valid proof of work, adding block {}.", + self.debug_output_id, b.hash()); + b.header.pow = proof; let opts = if self.config.cuckoo_size < consensus::DEFAULT_SIZESHIFT as u32 { chain::EASY_POW } else { @@ -129,7 +154,8 @@ impl Miner { self.chain_adapter.clone(), opts); if let Err(e) = res { - error!("Error validating mined block: {:?}", e); + error!("(Server ID: {}) Error validating mined block: {:?}", + self.debug_output_id, e); } else if let Ok(Some(tip)) = res { let chain_head = self.chain_head.clone(); let mut head = chain_head.lock().unwrap(); @@ -137,8 +163,9 @@ impl Miner { *head = tip; } } else { - debug!("No solution found after {} iterations, continuing...", - iter_count) + debug!("(Server ID: {}) No solution found after {} iterations, continuing...", + self.debug_output_id, + iter_count) } } } @@ -162,9 +189,11 @@ impl Miner { let txs = txs_box.iter().map(|tx| tx.as_ref()).collect(); let (output, kernel) = coinbase; let mut b = core::Block::with_reward(head, txs, output, kernel).unwrap(); - debug!("Built new block with {} inputs and {} outputs", + debug!("(Server ID: {}) Built new block with {} inputs and {} outputs, difficulty: {}", + self.debug_output_id, b.inputs.len(), - b.outputs.len()); + b.outputs.len(), + difficulty); // making sure we're not spending time mining a useless block let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); @@ -189,7 +218,8 @@ impl Miner { let request = WalletReceiveRequest::Coinbase(CbAmount{amount: consensus::REWARD}); let res: CbData = api::client::post(url.as_str(), &request) - .expect("Wallet receiver unreachable, could not claim reward. Is it running?"); + .expect(format!("(Server ID: {}) Wallet receiver unreachable, could not claim reward. Is it running?", + self.debug_output_id.as_str()).as_str()); let out_bin = util::from_hex(res.output).unwrap(); let kern_bin = util::from_hex(res.kernel).unwrap(); let output = ser::deserialize(&mut &out_bin[..]).unwrap(); diff --git a/grin/src/server.rs b/grin/src/server.rs index 16f8d33b2..57467ce62 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -153,11 +153,12 @@ impl Server { /// Start mining for blocks on a separate thread. Relies on a toy miner, /// mostly for testing. pub fn start_miner(&self, config: MinerConfig) { - let miner = miner::Miner::new(config, + let mut miner = miner::Miner::new(config, self.chain_head.clone(), self.chain_store.clone(), self.chain_adapter.clone(), self.tx_pool.clone()); + miner.set_debug_output_id(format!("Port {}",self.config.p2p_config.port)); thread::spawn(move || { miner.run_loop(); }); diff --git a/grin/src/types.rs b/grin/src/types.rs index 07e11fb67..98079e196 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -137,7 +137,7 @@ impl Default for MinerConfig { wallet_receiver_url: "http://localhost:13416".to_string(), burn_reward: false, slow_down_in_millis: 0, - cuckoo_size: 0, + cuckoo_size: 0 } } } diff --git a/grin/tests/framework.rs b/grin/tests/framework.rs index 00ff95acc..0447bf28c 100644 --- a/grin/tests/framework.rs +++ b/grin/tests/framework.rs @@ -53,7 +53,7 @@ use core::consensus; /// Just removes all results from previous runs pub fn clean_all_output(test_name_dir:&str){ - let target_dir = format!("target/{}", test_name_dir); + let target_dir = format!("target/test_servers/{}", test_name_dir); fs::remove_dir_all(target_dir); } @@ -414,11 +414,10 @@ impl LocalServerContainerPool { //Use self as coinbase wallet - if server_config.coinbase_wallet_address.len()==0 { - server_config.coinbase_wallet_address=String::from(format!("http://{}:{}", + server_config.coinbase_wallet_address=String::from(format!("http://{}:{}", server_config.base_addr, server_config.wallet_port)); - } + self.next_p2p_port+=1; self.next_api_port+=1; diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs index 303ce0db8..d232cd9af 100644 --- a/grin/tests/simulnet.rs +++ b/grin/tests/simulnet.rs @@ -130,12 +130,12 @@ fn simulate_parallel_mining(){ env_logger::init(); let test_name_dir="simulate_parallel_mining"; - framework::clean_all_output(test_name_dir); + //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; + pool_config.run_length_in_seconds = 60; //have to select different ports because of tests being run in parallel pool_config.base_api_port=30040; @@ -161,7 +161,7 @@ fn simulate_parallel_mining(){ server_config.p2p_server_port)); //And create 4 more, then let them run for a while - for i in 0..4 { + for i in 1..4 { //fudge in some slowdown server_config.miner_slowdown_in_millis = i*2; pool.create_server(&mut server_config);