diff --git a/api/src/types.rs b/api/src/types.rs index 74a6db7b7..82a2a5ca9 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -509,8 +509,8 @@ pub struct BlockHeaderPrintable { pub cuckoo_solution: Vec<u64>, /// Total accumulated difficulty since genesis block pub total_difficulty: u64, - /// Difficulty scaling factor between the different proofs of work - pub scaling_difficulty: u32, + /// Variable difficulty scaling factor for secondary proof of work + pub secondary_scaling: u32, /// Total kernel offset since genesis block pub total_kernel_offset: String, } @@ -531,7 +531,7 @@ impl BlockHeaderPrintable { edge_bits: h.pow.edge_bits(), cuckoo_solution: h.pow.proof.nonces.clone(), total_difficulty: h.pow.total_difficulty.to_num(), - scaling_difficulty: h.pow.scaling_difficulty, + secondary_scaling: h.pow.secondary_scaling, total_kernel_offset: h.total_kernel_offset.to_hex(), } } diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 060a0bb58..c2f4c8527 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -441,7 +441,13 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E return Err(ErrorKind::WrongTotalDifficulty.into()); } // check the secondary PoW scaling factor if applicable - if header.pow.scaling_difficulty != next_header_info.secondary_scaling { + if header.pow.secondary_scaling != next_header_info.secondary_scaling { + info!( + LOGGER, + "validate_header: header secondary scaling {} != {}", + header.pow.secondary_scaling, + next_header_info.secondary_scaling + ); return Err(ErrorKind::InvalidScaling.into()); } } diff --git a/chain/src/store.rs b/chain/src/store.rs index 423833812..3933b264c 100644 --- a/chain/src/store.rs +++ b/chain/src/store.rs @@ -650,7 +650,7 @@ impl<'a> Iterator for DifficultyIter<'a> { .clone() .map_or(Difficulty::zero(), |x| x.total_difficulty()); let difficulty = header.total_difficulty() - prev_difficulty; - let scaling = header.pow.scaling_difficulty; + let scaling = header.pow.secondary_scaling; Some(HeaderInfo::new( header.timestamp.timestamp() as u64, diff --git a/chain/tests/data_file_integrity.rs b/chain/tests/data_file_integrity.rs index f80a87386..f802f9b8c 100644 --- a/chain/tests/data_file_integrity.rs +++ b/chain/tests/data_file_integrity.rs @@ -89,7 +89,7 @@ fn data_files() { core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward) .unwrap(); b.header.timestamp = prev.timestamp + Duration::seconds(60); - b.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + b.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut b, false).unwrap(); diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 095d89dbd..faa67108b 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -71,7 +71,7 @@ fn mine_empty_chain() { core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward) .unwrap(); b.header.timestamp = prev.timestamp + Duration::seconds(60); - b.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + b.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut b, false).unwrap(); @@ -395,7 +395,7 @@ fn output_header_mappings() { core::core::Block::new(&prev, vec![], next_header_info.clone().difficulty, reward) .unwrap(); b.header.timestamp = prev.timestamp + Duration::seconds(60); - b.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + b.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut b, false).unwrap(); diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index c5005e251..79270ad26 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -72,7 +72,7 @@ fn test_coinbase_maturity() { let reward = libtx::reward::output(&keychain, &key_id1, 0, prev.height).unwrap(); let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap(); block.header.timestamp = prev.timestamp + Duration::seconds(60); - block.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + block.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut block, false).unwrap(); @@ -119,7 +119,7 @@ fn test_coinbase_maturity() { let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter()); block.header.timestamp = prev.timestamp + Duration::seconds(60); - block.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + block.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut block, false).unwrap(); @@ -152,7 +152,7 @@ fn test_coinbase_maturity() { let mut block = core::core::Block::new(&prev, vec![], Difficulty::min(), reward).unwrap(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter()); block.header.timestamp = prev.timestamp + Duration::seconds(60); - block.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + block.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut block, false).unwrap(); @@ -179,7 +179,7 @@ fn test_coinbase_maturity() { let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap(); block.header.timestamp = prev.timestamp + Duration::seconds(60); - block.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + block.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut block, false).unwrap(); diff --git a/core/src/consensus.rs b/core/src/consensus.rs index d903d1a88..8c6a4b9a8 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -221,7 +221,7 @@ impl HeaderInfo { timestamp, difficulty, secondary_scaling: global::initial_graph_weight(), - is_secondary: true, + is_secondary: false, } } @@ -232,7 +232,7 @@ impl HeaderInfo { timestamp: 1, difficulty, secondary_scaling, - is_secondary: true, + is_secondary: false, } } } diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 4089235d0..5405c3896 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -155,7 +155,7 @@ fn fixed_size_of_serialized_header(_version: u16) -> usize { size += mem::size_of::<u64>(); // output_mmr_size size += mem::size_of::<u64>(); // kernel_mmr_size size += mem::size_of::<Difficulty>(); // total_difficulty - size += mem::size_of::<u32>(); // scaling_difficulty + size += mem::size_of::<u32>(); // secondary_scaling size += mem::size_of::<u64>(); // nonce size } diff --git a/core/src/genesis.rs b/core/src/genesis.rs index cb4e88d3d..ac408e04d 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -45,7 +45,7 @@ pub fn genesis_testnet1() -> core::Block { timestamp: Utc.ymd(2017, 11, 16).and_hms(20, 0, 0), pow: ProofOfWork { total_difficulty: Difficulty::min(), - scaling_difficulty: 1, + secondary_scaling: 1, nonce: 28205, proof: Proof::new(vec![ 0x21e, 0x7a2, 0xeae, 0x144e, 0x1b1c, 0x1fbd, 0x203a, 0x214b, 0x293b, 0x2b74, @@ -67,7 +67,7 @@ pub fn genesis_testnet2() -> core::Block { timestamp: Utc.ymd(2018, 3, 26).and_hms(16, 0, 0), pow: ProofOfWork { total_difficulty: Difficulty::from_num(global::initial_block_difficulty()), - scaling_difficulty: 1, + secondary_scaling: 1, nonce: 1060, proof: Proof::new(vec![ 0x1940730, 0x333b9d0, 0x4739d6f, 0x4c6cfb1, 0x6e3d6c3, 0x74408a3, 0x7ba2bd2, @@ -90,7 +90,7 @@ pub fn genesis_testnet3() -> core::Block { timestamp: Utc.ymd(2018, 7, 8).and_hms(18, 0, 0), pow: ProofOfWork { total_difficulty: Difficulty::from_num(global::initial_block_difficulty()), - scaling_difficulty: 1, + secondary_scaling: 1, nonce: 4956988373127691, proof: Proof::new(vec![ 0xa420dc, 0xc8ffee, 0x10e433e, 0x1de9428, 0x2ed4cea, 0x52d907b, 0x5af0e3f, @@ -114,7 +114,7 @@ pub fn genesis_testnet4() -> core::Block { timestamp: Utc.ymd(2018, 10, 17).and_hms(20, 0, 0), pow: ProofOfWork { total_difficulty: Difficulty::from_num(global::initial_block_difficulty()), - scaling_difficulty: global::initial_graph_weight(), + secondary_scaling: global::initial_graph_weight(), nonce: 8612241555342799290, proof: Proof::new(vec![ 0x46f3b4, 0x1135f8c, 0x1a1596f, 0x1e10f71, 0x41c03ea, 0x63fe8e7, 0x65af34f, @@ -137,7 +137,7 @@ pub fn genesis_main() -> core::Block { timestamp: Utc.ymd(2018, 8, 14).and_hms(0, 0, 0), pow: ProofOfWork { total_difficulty: Difficulty::from_num(global::initial_block_difficulty()), - scaling_difficulty: 1, + secondary_scaling: 1, nonce: global::get_genesis_nonce(), proof: Proof::zero(consensus::PROOFSIZE), }, diff --git a/core/src/global.rs b/core/src/global.rs index 0002d82b2..db9967dc0 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -279,47 +279,26 @@ where let needed_block_count = DIFFICULTY_ADJUST_WINDOW as usize + 1; let mut last_n: Vec<HeaderInfo> = cursor.into_iter().take(needed_block_count).collect(); - // Sort blocks from earliest to latest (to keep conceptually easier) - last_n.reverse(); // Only needed just after blockchain launch... basically ensures there's // always enough data by simulating perfectly timed pre-genesis // blocks at the genesis difficulty as needed. - let block_count_difference = needed_block_count - last_n.len(); - if block_count_difference > 0 { - // Collect any real data we have - let mut live_intervals: Vec<HeaderInfo> = last_n - .iter() - .map(|b| HeaderInfo::from_ts_diff(b.timestamp, b.difficulty)) - .collect(); - for i in (1..live_intervals.len()).rev() { - // prevents issues with very fast automated test chains - if live_intervals[i - 1].timestamp > live_intervals[i].timestamp { - live_intervals[i].timestamp = 0; - } else { - live_intervals[i].timestamp = - live_intervals[i].timestamp - live_intervals[i - 1].timestamp; - } - } - // Remove genesis "interval" - if live_intervals.len() > 1 { - live_intervals.remove(0); + let n = last_n.len(); + if needed_block_count > n { + let last_ts_delta = if n > 1 { + last_n[0].timestamp - last_n[1].timestamp } else { - //if it's just genesis, adjust the interval - live_intervals[0].timestamp = BLOCK_TIME_SEC; - } - let mut interval_index = live_intervals.len() - 1; - let mut last_ts = last_n.first().unwrap().timestamp; - let last_diff = live_intervals[live_intervals.len() - 1].difficulty; - // fill in simulated blocks with values from the previous real block + BLOCK_TIME_SEC + }; + let last_diff = last_n[0].difficulty; - for _ in 0..block_count_difference { - last_ts = last_ts.saturating_sub(live_intervals[live_intervals.len() - 1].timestamp); - last_n.insert(0, HeaderInfo::from_ts_diff(last_ts, last_diff.clone())); - interval_index = match interval_index { - 0 => live_intervals.len() - 1, - _ => interval_index - 1, - }; + // fill in simulated blocks with values from the previous real block + let mut last_ts = last_n.last().unwrap().timestamp; + for _ in n..needed_block_count { + last_ts = last_ts.saturating_sub(last_ts_delta); + last_n.push(HeaderInfo::from_ts_diff(last_ts, last_diff.clone())); } } + last_n.reverse(); + assert_eq!(last_n.len(), needed_block_count); last_n } diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 57d4b220b..10caf6ce9 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -218,8 +218,8 @@ impl<'de> de::Visitor<'de> for DiffVisitor { pub struct ProofOfWork { /// Total accumulated difficulty since genesis block pub total_difficulty: Difficulty, - /// Difficulty scaling factor between the different proofs of work - pub scaling_difficulty: u32, + /// Variable difficulty scaling factor fo secondary proof of work + pub secondary_scaling: u32, /// Nonce increment used to mine this block. pub nonce: u64, /// Proof of work data. @@ -231,7 +231,7 @@ impl Default for ProofOfWork { let proof_size = global::proofsize(); ProofOfWork { total_difficulty: Difficulty::min(), - scaling_difficulty: 1, + secondary_scaling: 1, nonce: 0, proof: Proof::zero(proof_size), } @@ -242,12 +242,12 @@ impl ProofOfWork { /// Read implementation, can't define as trait impl as we need a version pub fn read(_ver: u16, reader: &mut Reader) -> Result<ProofOfWork, ser::Error> { let total_difficulty = Difficulty::read(reader)?; - let scaling_difficulty = reader.read_u32()?; + let secondary_scaling = reader.read_u32()?; let nonce = reader.read_u64()?; let proof = Proof::read(reader)?; Ok(ProofOfWork { total_difficulty, - scaling_difficulty, + secondary_scaling, nonce, proof, }) @@ -269,7 +269,7 @@ impl ProofOfWork { ser_multiwrite!( writer, [write_u64, self.total_difficulty.to_num()], - [write_u32, self.scaling_difficulty] + [write_u32, self.secondary_scaling] ); Ok(()) } @@ -279,7 +279,7 @@ impl ProofOfWork { // 2 proof of works, Cuckoo29 (for now) and Cuckoo30+, which are scaled // differently (scaling not controlled for now) if self.proof.edge_bits == SECOND_POW_EDGE_BITS { - Difficulty::from_proof_scaled(&self.proof, self.scaling_difficulty) + Difficulty::from_proof_scaled(&self.proof, self.secondary_scaling) } else { Difficulty::from_proof_adjusted(&self.proof) } diff --git a/core/tests/consensus.rs b/core/tests/consensus.rs index 9dc5108cb..daae4e307 100644 --- a/core/tests/consensus.rs +++ b/core/tests/consensus.rs @@ -400,6 +400,12 @@ fn next_target_adjustment() { Difficulty::from_num(10000) ); + // check pre difficulty_data_to_vector effect on retargetting + assert_eq!( + next_difficulty(1, vec![HeaderInfo::from_ts_diff(42, hi.difficulty)]).difficulty, + Difficulty::from_num(14913) + ); + // checking averaging works hi.difficulty = Difficulty::from_num(500); let sec = DIFFICULTY_ADJUST_WINDOW / 2; diff --git a/doc/api/node_api.md b/doc/api/node_api.md index 65d14fee6..f158fda4f 100644 --- a/doc/api/node_api.md +++ b/doc/api/node_api.md @@ -82,7 +82,7 @@ Optionally return results as "compact blocks" by passing `?compact` query. | - edge_bits | number | Size of the cuckoo graph (2_log of number of edges) | | - cuckoo_solution | []number | The Cuckoo solution for this block | | - total_difficulty | number | Total accumulated difficulty since genesis block | - | - scaling_difficulty | number | Difficulty scaling factor between the different proofs of work | + | - secondary_scaling | number | Variable difficulty scaling factor for secondary proof of work | | - total_kernel_offset | string | Total kernel offset since genesis block | | inputs | []string | Input transactions | | outputs | []object | Outputs transactions | diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index b98234517..b8c3ce4ec 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -132,7 +132,7 @@ fn build_block( b.validate(&head.total_kernel_offset, verifier_cache)?; b.header.pow.nonce = thread_rng().gen(); - b.header.pow.scaling_difficulty = difficulty.secondary_scaling; + b.header.pow.secondary_scaling = difficulty.secondary_scaling; b.header.timestamp = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(now_sec, 0), Utc); let b_difficulty = (b.header.total_difficulty() - head.total_difficulty()).to_num(); diff --git a/wallet/tests/common/mod.rs b/wallet/tests/common/mod.rs index 07cf75ff8..3cd019f86 100644 --- a/wallet/tests/common/mod.rs +++ b/wallet/tests/common/mod.rs @@ -97,7 +97,7 @@ pub fn add_block_with_reward(chain: &Chain, txs: Vec<&Transaction>, reward: CbDa (output, kernel), ).unwrap(); b.header.timestamp = prev.timestamp + Duration::seconds(60); - b.header.pow.scaling_difficulty = next_header_info.secondary_scaling; + b.header.pow.secondary_scaling = next_header_info.secondary_scaling; chain.set_txhashset_roots(&mut b, false).unwrap(); pow::pow_size( &mut b.header,