BitcoinCash: extracted work_reuired to separate file
This commit is contained in:
parent
261bd518fd
commit
15449f69bb
|
@ -75,6 +75,7 @@ mod error;
|
|||
mod sigops;
|
||||
mod timestamp;
|
||||
mod work;
|
||||
mod work_bch;
|
||||
|
||||
// pre-verification
|
||||
mod verify_block;
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use std::cmp;
|
||||
use primitives::compact::Compact;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::{Uint, U256};
|
||||
use chain::BlockHeader;
|
||||
use primitives::bigint::U256;
|
||||
use chain::{IndexedBlockHeader, BlockHeader};
|
||||
use network::{Network, ConsensusParams, ConsensusFork};
|
||||
use db::{BlockHeaderProvider, BlockRef};
|
||||
use timestamp::median_timestamp_inclusive;
|
||||
use work_bch::work_required_bitcoin_cash;
|
||||
|
||||
use constants::{
|
||||
TARGET_SPACING_SECONDS, DOUBLE_SPACING_SECONDS,
|
||||
TARGET_TIMESPAN_SECONDS, MIN_TIMESPAN, MAX_TIMESPAN, RETARGETING_INTERVAL
|
||||
DOUBLE_SPACING_SECONDS, TARGET_TIMESPAN_SECONDS,
|
||||
MIN_TIMESPAN, MAX_TIMESPAN, RETARGETING_INTERVAL
|
||||
};
|
||||
|
||||
pub fn is_retarget_height(height: u32) -> bool {
|
||||
|
@ -65,31 +65,17 @@ pub fn work_required(parent_hash: H256, time: u32, height: u32, store: &BlockHea
|
|||
|
||||
let parent_header = store.block_header(parent_hash.clone().into()).expect("self.height != 0; qed");
|
||||
|
||||
// special processing of Bitcoin Cash difficulty adjustment hardfork (Nov 2017), where difficulty is adjusted after each block
|
||||
let parent_timestamp = match consensus.fork {
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => {
|
||||
let parent_timestamp = median_timestamp_inclusive(parent_header.hash(), store);
|
||||
if parent_timestamp >= fork.difficulty_adjustion_time {
|
||||
return work_required_bitcoin_cash_adjusted(parent_header, time, height, store, consensus);
|
||||
}
|
||||
|
||||
Some(parent_timestamp)
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
match consensus.fork {
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height =>
|
||||
return work_required_bitcoin_cash(IndexedBlockHeader {
|
||||
hash: parent_hash,
|
||||
raw: parent_header
|
||||
}, time, height, store, consensus, fork, max_bits),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if is_retarget_height(height) {
|
||||
let retarget_ref = (height - RETARGETING_INTERVAL).into();
|
||||
let retarget_header = store.block_header(retarget_ref).expect("self.height != 0 && self.height % RETARGETING_INTERVAL == 0; qed");
|
||||
|
||||
// timestamp of block(height - RETARGETING_INTERVAL)
|
||||
let retarget_timestamp = retarget_header.time;
|
||||
// timestamp of parent block
|
||||
let last_timestamp = parent_header.time;
|
||||
// bits of last block
|
||||
let last_bits = parent_header.bits;
|
||||
|
||||
return work_required_retarget(max_bits, retarget_timestamp, last_timestamp, last_bits);
|
||||
return work_required_retarget(parent_header, height, store, max_bits);
|
||||
}
|
||||
|
||||
if consensus.network == Network::Testnet {
|
||||
|
@ -98,33 +84,6 @@ pub fn work_required(parent_hash: H256, time: u32, height: u32, store: &BlockHea
|
|||
|
||||
match consensus.fork {
|
||||
_ if parent_header.bits == max_bits => parent_header.bits,
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => {
|
||||
// REQ-7 Difficulty adjustement in case of hashrate drop
|
||||
// In case the MTP of the tip of the chain is 12h or more after the MTP 6 block before the tip,
|
||||
// the proof of work target is increased by a quarter, or 25%, which corresponds to a difficulty
|
||||
// reduction of 20%.
|
||||
let ancient_block_ref = (height - 6 - 1).into();
|
||||
let ancient_header = store.block_header(ancient_block_ref)
|
||||
.expect("parent_header.bits != max_bits; difficulty is max_bits for first RETARGETING_INTERVAL height; RETARGETING_INTERVAL > 7; qed");
|
||||
|
||||
let parent_timestamp = parent_timestamp
|
||||
.expect("we are in BitcoinCash-HF branch; parent_timestamp is calculated for BitcoinCash-HF branch above; qed");
|
||||
let ancient_timestamp = median_timestamp_inclusive(ancient_header.hash(), store);
|
||||
let timestamp_diff = parent_timestamp.checked_sub(ancient_timestamp).unwrap_or_default();
|
||||
if timestamp_diff < 43_200 {
|
||||
// less than 12h => no difficulty change needed
|
||||
return parent_header.bits;
|
||||
}
|
||||
|
||||
let mut new_bits: U256 = parent_header.bits.into();
|
||||
let max_bits: U256 = max_bits.into();
|
||||
new_bits = new_bits + (new_bits >> 2);
|
||||
if new_bits > max_bits {
|
||||
new_bits = max_bits
|
||||
}
|
||||
|
||||
new_bits.into()
|
||||
},
|
||||
_ => parent_header.bits,
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +121,17 @@ pub fn work_required_testnet(parent_hash: H256, time: u32, height: u32, store: &
|
|||
}
|
||||
|
||||
/// Algorithm used for retargeting work every 2 weeks
|
||||
pub fn work_required_retarget(max_work_bits: Compact, retarget_timestamp: u32, last_timestamp: u32, last_bits: Compact) -> Compact {
|
||||
pub fn work_required_retarget(parent_header: BlockHeader, height: u32, store: &BlockHeaderProvider, max_work_bits: Compact) -> Compact {
|
||||
let retarget_ref = (height - RETARGETING_INTERVAL).into();
|
||||
let retarget_header = store.block_header(retarget_ref).expect("self.height != 0 && self.height % RETARGETING_INTERVAL == 0; qed");
|
||||
|
||||
// timestamp of block(height - RETARGETING_INTERVAL)
|
||||
let retarget_timestamp = retarget_header.time;
|
||||
// timestamp of parent block
|
||||
let last_timestamp = parent_header.time;
|
||||
// bits of last block
|
||||
let last_bits = parent_header.bits;
|
||||
|
||||
let mut retarget: U256 = last_bits.into();
|
||||
let maximum: U256 = max_work_bits.into();
|
||||
|
||||
|
@ -176,120 +145,6 @@ pub fn work_required_retarget(max_work_bits: Compact, retarget_timestamp: u32, l
|
|||
}
|
||||
}
|
||||
|
||||
/// Algorithm to adjust difficulty after each block. Implementation is based on Bitcoin ABC commit:
|
||||
/// https://github.com/Bitcoin-ABC/bitcoin-abc/commit/be51cf295c239ff6395a0aa67a3e13906aca9cb2
|
||||
pub fn work_required_bitcoin_cash_adjusted(parent_header: BlockHeader, time: u32, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams) -> Compact {
|
||||
/// To reduce the impact of timestamp manipulation, we select the block we are
|
||||
/// basing our computation on via a median of 3.
|
||||
fn suitable_block(mut header2: BlockHeader, store: &BlockHeaderProvider) -> BlockHeader {
|
||||
let reason = "header.height >= RETARGETNG_INTERVAL; RETARGETING_INTERVAL > 2; qed";
|
||||
let mut header1 = store.block_header(header2.previous_header_hash.clone().into()).expect(reason);
|
||||
let mut header0 = store.block_header(header1.previous_header_hash.clone().into()).expect(reason);
|
||||
|
||||
if header0.time > header2.time {
|
||||
::std::mem::swap(&mut header0, &mut header2);
|
||||
}
|
||||
if header0.time > header1.time {
|
||||
::std::mem::swap(&mut header0, &mut header1);
|
||||
}
|
||||
if header1.time > header2.time {
|
||||
::std::mem::swap(&mut header1, &mut header2);
|
||||
}
|
||||
|
||||
header1
|
||||
}
|
||||
|
||||
/// Get block proof.
|
||||
fn block_proof(header: &BlockHeader) -> U256 {
|
||||
let proof: U256 = header.bits.into();
|
||||
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
|
||||
// as it's too large for a arith_uint256. However, as 2**256 is at least as
|
||||
// large as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) /
|
||||
// (bnTarget+1)) + 1, or ~bnTarget / (nTarget+1) + 1.
|
||||
(!proof / (proof + U256::one())) + U256::one()
|
||||
}
|
||||
|
||||
/// Compute chain work between two blocks. Last block work is included. First block work is excluded.
|
||||
fn compute_work_between_blocks(first: H256, last: &BlockHeader, store: &BlockHeaderProvider) -> U256 {
|
||||
debug_assert!(last.hash() != first);
|
||||
let mut chain_work: U256 = block_proof(last);
|
||||
let mut prev_hash = last.previous_header_hash.clone();
|
||||
loop {
|
||||
let header = store.block_header(prev_hash.into())
|
||||
.expect("last header is on main chain; first is at height last.height - 144; it is on main chain; qed");
|
||||
|
||||
chain_work = chain_work + block_proof(&header);
|
||||
prev_hash = header.previous_header_hash;
|
||||
if prev_hash == first {
|
||||
return chain_work;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the a target based on the work done between 2 blocks and the time
|
||||
/// required to produce that work.
|
||||
fn compute_target(first_header: BlockHeader, last_header: BlockHeader, store: &BlockHeaderProvider) -> U256 {
|
||||
// From the total work done and the time it took to produce that much work,
|
||||
// we can deduce how much work we expect to be produced in the targeted time
|
||||
// between blocks.
|
||||
let mut work = compute_work_between_blocks(first_header.hash(), &last_header, store);
|
||||
work = work * TARGET_SPACING_SECONDS.into();
|
||||
|
||||
// In order to avoid difficulty cliffs, we bound the amplitude of the
|
||||
// adjustement we are going to do.
|
||||
debug_assert!(last_header.time > first_header.time);
|
||||
let mut actual_timespan = last_header.time - first_header.time;
|
||||
if actual_timespan > 288 * TARGET_SPACING_SECONDS {
|
||||
actual_timespan = 288 * TARGET_SPACING_SECONDS;
|
||||
} else if actual_timespan < 72 * TARGET_SPACING_SECONDS {
|
||||
actual_timespan = 72 * TARGET_SPACING_SECONDS;
|
||||
}
|
||||
|
||||
let work = work / actual_timespan.into();
|
||||
|
||||
// We need to compute T = (2^256 / W) - 1 but 2^256 doesn't fit in 256 bits.
|
||||
// By expressing 1 as W / W, we get (2^256 - W) / W, and we can compute
|
||||
// 2^256 - W as the complement of W.
|
||||
(!work) / work
|
||||
}
|
||||
|
||||
// This cannot handle the genesis block and early blocks in general.
|
||||
debug_assert!(height > 0);
|
||||
|
||||
// Special difficulty rule for testnet:
|
||||
// If the new block's timestamp is more than 2 * 10 minutes then allow
|
||||
// mining of a min-difficulty block.
|
||||
let max_bits = consensus.network.max_bits();
|
||||
if consensus.network == Network::Testnet {
|
||||
let max_time_gap = parent_header.time + DOUBLE_SPACING_SECONDS;
|
||||
if time > max_time_gap {
|
||||
return max_bits.into();
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the difficulty based on the full adjustement interval.
|
||||
let last_height = height - 1;
|
||||
debug_assert!(last_height >= RETARGETING_INTERVAL);
|
||||
|
||||
// Get the last suitable block of the difficulty interval.
|
||||
let last_header = suitable_block(parent_header, store);
|
||||
|
||||
// Get the first suitable block of the difficulty interval.
|
||||
let first_height = last_height - 144;
|
||||
let first_header = store.block_header(first_height.into())
|
||||
.expect("last_height >= RETARGETING_INTERVAL; RETARGETING_INTERVAL - 144 > 0; qed");
|
||||
let first_header = suitable_block(first_header, store);
|
||||
|
||||
// Compute the target based on time and work done during the interval.
|
||||
let next_target = compute_target(first_header, last_header, store);
|
||||
let max_bits = consensus.network.max_bits();
|
||||
if next_target > max_bits {
|
||||
return max_bits.into();
|
||||
}
|
||||
|
||||
next_target.into()
|
||||
}
|
||||
|
||||
pub fn block_reward_satoshi(block_height: u32) -> u64 {
|
||||
let mut res = 50 * 100 * 1000 * 1000;
|
||||
for _ in 0..block_height / 210000 { res /= 2 }
|
||||
|
@ -298,42 +153,10 @@ pub fn block_reward_satoshi(block_height: u32) -> u64 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use primitives::bytes::Bytes;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::U256;
|
||||
use primitives::compact::Compact;
|
||||
use network::{Network, ConsensusParams, BitcoinCashConsensusParams, ConsensusFork};
|
||||
use db::{BlockHeaderProvider, BlockRef};
|
||||
use chain::BlockHeader;
|
||||
use super::{work_required, is_valid_proof_of_work_hash, is_valid_proof_of_work,
|
||||
block_reward_satoshi, work_required_bitcoin_cash_adjusted};
|
||||
|
||||
#[derive(Default)]
|
||||
struct MemoryBlockHeaderProvider {
|
||||
pub by_height: Vec<BlockHeader>,
|
||||
pub by_hash: HashMap<H256, usize>,
|
||||
}
|
||||
|
||||
impl MemoryBlockHeaderProvider {
|
||||
pub fn insert(&mut self, header: BlockHeader) {
|
||||
self.by_hash.insert(header.hash(), self.by_height.len());
|
||||
self.by_height.push(header);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeaderProvider for MemoryBlockHeaderProvider {
|
||||
fn block_header_bytes(&self, _block_ref: BlockRef) -> Option<Bytes> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn block_header(&self, block_ref: BlockRef) -> Option<BlockHeader> {
|
||||
match block_ref {
|
||||
BlockRef::Hash(ref hash) => self.by_hash.get(hash).map(|h| &self.by_height[*h]).cloned(),
|
||||
BlockRef::Number(height) => self.by_height.get(height as usize).cloned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
use network::Network;
|
||||
use super::{is_valid_proof_of_work_hash, is_valid_proof_of_work, block_reward_satoshi};
|
||||
|
||||
fn is_valid_pow(max: Compact, bits: u32, hash: &'static str) -> bool {
|
||||
is_valid_proof_of_work_hash(bits.into(), &H256::from_reversed_str(hash)) &&
|
||||
|
@ -364,281 +187,4 @@ mod tests {
|
|||
assert_eq!(block_reward_satoshi(630000), 625000000);
|
||||
assert_eq!(block_reward_satoshi(630001), 625000000);
|
||||
}
|
||||
|
||||
// original test link:
|
||||
// https://github.com/bitcoinclassic/bitcoinclassic/blob/8bf1fb856df44d1b790b0b835e4c1969be736e25/src/test/pow_tests.cpp#L108
|
||||
#[test]
|
||||
fn bitcoin_cash_req7() {
|
||||
let main_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::NoFork);
|
||||
let uahf_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(BitcoinCashConsensusParams {
|
||||
height: 1000,
|
||||
difficulty_adjustion_time: 0xffffffff,
|
||||
}));
|
||||
let mut header_provider = MemoryBlockHeaderProvider::default();
|
||||
header_provider.insert(BlockHeader {
|
||||
version: 0,
|
||||
previous_header_hash: 0.into(),
|
||||
merkle_root_hash: 0.into(),
|
||||
time: 1269211443,
|
||||
bits: 0x207fffff.into(),
|
||||
nonce: 0,
|
||||
});
|
||||
|
||||
// create x100 pre-HF blocks
|
||||
for height in 1..1000 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 10 * 60;
|
||||
header_provider.insert(header);
|
||||
}
|
||||
|
||||
// create x10 post-HF blocks every 2 hours
|
||||
// MTP still less than 12h
|
||||
for height in 1000..1010 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 60 * 60;
|
||||
header_provider.insert(header.clone());
|
||||
|
||||
let main_bits: u32 = work_required(header.hash(), 0, height as u32, &header_provider, &main_consensus).into();
|
||||
assert_eq!(main_bits, 0x207fffff_u32);
|
||||
let uahf_bits: u32 = work_required(header.hash(), 0, height as u32, &header_provider, &uahf_consensus).into();
|
||||
assert_eq!(uahf_bits, 0x207fffff_u32);
|
||||
}
|
||||
|
||||
// MTP becames greater than 12h
|
||||
let mut header = header_provider.block_header(1009.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 60 * 60;
|
||||
header_provider.insert(header.clone());
|
||||
|
||||
let main_bits: u32 = work_required(header.hash(), 0, 1010, &header_provider, &main_consensus).into();
|
||||
assert_eq!(main_bits, 0x207fffff_u32);
|
||||
let uahf_bits: u32 = work_required(header.hash(), 0, 1010, &header_provider, &uahf_consensus).into();
|
||||
assert_eq!(uahf_bits, 0x1d00ffff_u32);
|
||||
}
|
||||
|
||||
// original test link:
|
||||
// https://github.com/Bitcoin-ABC/bitcoin-abc/blob/d8eac91f8d16716eed0ad11ccac420122280bb13/src/test/pow_tests.cpp#L193
|
||||
#[test]
|
||||
fn bitcoin_cash_adjusted_difficulty() {
|
||||
let uahf_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(BitcoinCashConsensusParams {
|
||||
height: 1000,
|
||||
difficulty_adjustion_time: 0xffffffff,
|
||||
}));
|
||||
|
||||
|
||||
let limit_bits = uahf_consensus.network.max_bits();
|
||||
let initial_bits = limit_bits >> 4;
|
||||
let mut header_provider = MemoryBlockHeaderProvider::default();
|
||||
|
||||
// Genesis block.
|
||||
header_provider.insert(BlockHeader {
|
||||
version: 0,
|
||||
previous_header_hash: 0.into(),
|
||||
merkle_root_hash: 0.into(),
|
||||
time: 1269211443,
|
||||
bits: initial_bits.into(),
|
||||
nonce: 0,
|
||||
});
|
||||
|
||||
// Pile up some blocks every 10 mins to establish some history.
|
||||
for height in 1..2050 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header_provider.insert(header);
|
||||
}
|
||||
|
||||
// Difficulty stays the same as long as we produce a block every 10 mins.
|
||||
let current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2049.into()).unwrap(),
|
||||
0, 2050, &header_provider, &uahf_consensus);
|
||||
for height in 2050..2060 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
}
|
||||
|
||||
// Make sure we skip over blocks that are out of wack. To do so, we produce
|
||||
// a block that is far in the future
|
||||
let mut header = header_provider.block_header(2059.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2060.into()).unwrap(),
|
||||
0, 2061, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// .. and then produce a block with the expected timestamp.
|
||||
let mut header = header_provider.block_header(2060.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 600 - 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2061.into()).unwrap(),
|
||||
0, 2062, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// The system should continue unaffected by the block with a bogous timestamps.
|
||||
for height in 2062..2082 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
}
|
||||
|
||||
// We start emitting blocks slightly faster. The first block has no impact.
|
||||
let mut header = header_provider.block_header(2081.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 550;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2082.into()).unwrap(),
|
||||
0, 2083, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// Now we should see difficulty increase slowly.
|
||||
let mut current_bits = current_bits;
|
||||
for height in 2083..2093 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 550;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < current_work);
|
||||
debug_assert!((current_work - calculated_work) < (current_work >> 10));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0fe7b1.into());
|
||||
|
||||
// If we dramatically shorten block production, difficulty increases faster.
|
||||
for height in 2093..2113 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 10;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < current_work);
|
||||
debug_assert!((current_work - calculated_work) < (current_work >> 4));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0db19f.into());
|
||||
|
||||
// We start to emit blocks significantly slower. The first block has no
|
||||
// impact.
|
||||
let mut header = header_provider.block_header(2112.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let mut current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2113.into()).unwrap(),
|
||||
0, 2114, &header_provider, &uahf_consensus);
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0d9222.into());
|
||||
|
||||
// If we dramatically slow down block production, difficulty decreases.
|
||||
for height in 2114..2207 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < limit_bits);
|
||||
debug_assert!(calculated_work > current_work);
|
||||
debug_assert!((calculated_work - current_work) < (current_work >> 3));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c2f13b9.into());
|
||||
|
||||
// Due to the window of time being bounded, next block's difficulty actually
|
||||
// gets harder.
|
||||
let mut header = header_provider.block_header(2206.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let mut current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2207.into()).unwrap(),
|
||||
0, 2208, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(current_bits, 0x1c2ee9bf.into());
|
||||
|
||||
// And goes down again. It takes a while due to the window being bounded and
|
||||
// the skewed block causes 2 blocks to get out of the window.
|
||||
for height in 2208..2400 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work <= limit_bits);
|
||||
debug_assert!(calculated_work > current_work);
|
||||
debug_assert!((calculated_work - current_work) < (current_work >> 3));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1d00ffff.into());
|
||||
|
||||
// Once the difficulty reached the minimum allowed level, it doesn't get any
|
||||
// easier.
|
||||
for height in 2400..2405 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, limit_bits.into());
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,487 @@
|
|||
use primitives::compact::Compact;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::{Uint, U256};
|
||||
use chain::{IndexedBlockHeader, BlockHeader};
|
||||
use network::{Network, ConsensusParams, BitcoinCashConsensusParams};
|
||||
use db::BlockHeaderProvider;
|
||||
use timestamp::median_timestamp_inclusive;
|
||||
use work::{is_retarget_height, work_required_testnet, work_required_retarget};
|
||||
|
||||
use constants::{
|
||||
DOUBLE_SPACING_SECONDS, TARGET_SPACING_SECONDS, RETARGETING_INTERVAL
|
||||
};
|
||||
|
||||
/// Returns work required for given header for the post-HF Bitcoin Cash block
|
||||
pub fn work_required_bitcoin_cash(parent_header: IndexedBlockHeader, time: u32, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams, fork: &BitcoinCashConsensusParams, max_bits: Compact) -> Compact {
|
||||
// special processing of Bitcoin Cash difficulty adjustment hardfork (Nov 2017), where difficulty is adjusted after each block
|
||||
let parent_timestamp = median_timestamp_inclusive(parent_header.hash.clone(), store);
|
||||
if parent_timestamp >= fork.difficulty_adjustion_time {
|
||||
return work_required_bitcoin_cash_adjusted(parent_header, time, height, store, consensus);
|
||||
}
|
||||
|
||||
if is_retarget_height(height) {
|
||||
return work_required_retarget(parent_header.raw, height, store, max_bits);
|
||||
}
|
||||
|
||||
if consensus.network == Network::Testnet {
|
||||
return work_required_testnet(parent_header.hash, time, height, store, Network::Testnet)
|
||||
}
|
||||
|
||||
if parent_header.raw.bits == max_bits {
|
||||
return parent_header.raw.bits;
|
||||
}
|
||||
|
||||
// REQ-7 Difficulty adjustement in case of hashrate drop
|
||||
// In case the MTP of the tip of the chain is 12h or more after the MTP 6 block before the tip,
|
||||
// the proof of work target is increased by a quarter, or 25%, which corresponds to a difficulty
|
||||
// reduction of 20%.
|
||||
let ancient_block_ref = (height - 6 - 1).into();
|
||||
let ancient_header = store.block_header(ancient_block_ref)
|
||||
.expect("parent_header.bits != max_bits; difficulty is max_bits for first RETARGETING_INTERVAL height; RETARGETING_INTERVAL > 7; qed");
|
||||
|
||||
let ancient_timestamp = median_timestamp_inclusive(ancient_header.hash(), store);
|
||||
let timestamp_diff = parent_timestamp.checked_sub(ancient_timestamp).unwrap_or_default();
|
||||
if timestamp_diff < 43_200 {
|
||||
// less than 12h => no difficulty change needed
|
||||
return parent_header.raw.bits;
|
||||
}
|
||||
|
||||
let mut new_bits: U256 = parent_header.raw.bits.into();
|
||||
let max_bits: U256 = max_bits.into();
|
||||
new_bits = new_bits + (new_bits >> 2);
|
||||
if new_bits > max_bits {
|
||||
new_bits = max_bits
|
||||
}
|
||||
|
||||
new_bits.into()
|
||||
}
|
||||
|
||||
/// Algorithm to adjust difficulty after each block. Implementation is based on Bitcoin ABC commit:
|
||||
/// https://github.com/Bitcoin-ABC/bitcoin-abc/commit/be51cf295c239ff6395a0aa67a3e13906aca9cb2
|
||||
fn work_required_bitcoin_cash_adjusted(parent_header: IndexedBlockHeader, time: u32, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams) -> Compact {
|
||||
/// To reduce the impact of timestamp manipulation, we select the block we are
|
||||
/// basing our computation on via a median of 3.
|
||||
fn suitable_block(mut header2: BlockHeader, store: &BlockHeaderProvider) -> BlockHeader {
|
||||
let reason = "header.height >= RETARGETNG_INTERVAL; RETARGETING_INTERVAL > 2; qed";
|
||||
let mut header1 = store.block_header(header2.previous_header_hash.clone().into()).expect(reason);
|
||||
let mut header0 = store.block_header(header1.previous_header_hash.clone().into()).expect(reason);
|
||||
|
||||
if header0.time > header2.time {
|
||||
::std::mem::swap(&mut header0, &mut header2);
|
||||
}
|
||||
if header0.time > header1.time {
|
||||
::std::mem::swap(&mut header0, &mut header1);
|
||||
}
|
||||
if header1.time > header2.time {
|
||||
::std::mem::swap(&mut header1, &mut header2);
|
||||
}
|
||||
|
||||
header1
|
||||
}
|
||||
|
||||
/// Get block proof.
|
||||
fn block_proof(header: &BlockHeader) -> U256 {
|
||||
let proof: U256 = header.bits.into();
|
||||
// We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256
|
||||
// as it's too large for a arith_uint256. However, as 2**256 is at least as
|
||||
// large as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) /
|
||||
// (bnTarget+1)) + 1, or ~bnTarget / (nTarget+1) + 1.
|
||||
(!proof / (proof + U256::one())) + U256::one()
|
||||
}
|
||||
|
||||
/// Compute chain work between two blocks. Last block work is included. First block work is excluded.
|
||||
fn compute_work_between_blocks(first: H256, last: &BlockHeader, store: &BlockHeaderProvider) -> U256 {
|
||||
debug_assert!(last.hash() != first);
|
||||
let mut chain_work: U256 = block_proof(last);
|
||||
let mut prev_hash = last.previous_header_hash.clone();
|
||||
loop {
|
||||
let header = store.block_header(prev_hash.into())
|
||||
.expect("last header is on main chain; first is at height last.height - 144; it is on main chain; qed");
|
||||
|
||||
chain_work = chain_work + block_proof(&header);
|
||||
prev_hash = header.previous_header_hash;
|
||||
if prev_hash == first {
|
||||
return chain_work;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the a target based on the work done between 2 blocks and the time
|
||||
/// required to produce that work.
|
||||
fn compute_target(first_header: BlockHeader, last_header: BlockHeader, store: &BlockHeaderProvider) -> U256 {
|
||||
// From the total work done and the time it took to produce that much work,
|
||||
// we can deduce how much work we expect to be produced in the targeted time
|
||||
// between blocks.
|
||||
let mut work = compute_work_between_blocks(first_header.hash(), &last_header, store);
|
||||
work = work * TARGET_SPACING_SECONDS.into();
|
||||
|
||||
// In order to avoid difficulty cliffs, we bound the amplitude of the
|
||||
// adjustement we are going to do.
|
||||
debug_assert!(last_header.time > first_header.time);
|
||||
let mut actual_timespan = last_header.time - first_header.time;
|
||||
if actual_timespan > 288 * TARGET_SPACING_SECONDS {
|
||||
actual_timespan = 288 * TARGET_SPACING_SECONDS;
|
||||
} else if actual_timespan < 72 * TARGET_SPACING_SECONDS {
|
||||
actual_timespan = 72 * TARGET_SPACING_SECONDS;
|
||||
}
|
||||
|
||||
let work = work / actual_timespan.into();
|
||||
|
||||
// We need to compute T = (2^256 / W) - 1 but 2^256 doesn't fit in 256 bits.
|
||||
// By expressing 1 as W / W, we get (2^256 - W) / W, and we can compute
|
||||
// 2^256 - W as the complement of W.
|
||||
(!work) / work
|
||||
}
|
||||
|
||||
// This cannot handle the genesis block and early blocks in general.
|
||||
debug_assert!(height > 0);
|
||||
|
||||
// Special difficulty rule for testnet:
|
||||
// If the new block's timestamp is more than 2 * 10 minutes then allow
|
||||
// mining of a min-difficulty block.
|
||||
let max_bits = consensus.network.max_bits();
|
||||
if consensus.network == Network::Testnet {
|
||||
let max_time_gap = parent_header.raw.time + DOUBLE_SPACING_SECONDS;
|
||||
if time > max_time_gap {
|
||||
return max_bits.into();
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the difficulty based on the full adjustement interval.
|
||||
let last_height = height - 1;
|
||||
debug_assert!(last_height >= RETARGETING_INTERVAL);
|
||||
|
||||
// Get the last suitable block of the difficulty interval.
|
||||
let last_header = suitable_block(parent_header.raw, store);
|
||||
|
||||
// Get the first suitable block of the difficulty interval.
|
||||
let first_height = last_height - 144;
|
||||
let first_header = store.block_header(first_height.into())
|
||||
.expect("last_height >= RETARGETING_INTERVAL; RETARGETING_INTERVAL - 144 > 0; qed");
|
||||
let first_header = suitable_block(first_header, store);
|
||||
|
||||
// Compute the target based on time and work done during the interval.
|
||||
let next_target = compute_target(first_header, last_header, store);
|
||||
let max_bits = consensus.network.max_bits();
|
||||
if next_target > max_bits {
|
||||
return max_bits.into();
|
||||
}
|
||||
|
||||
next_target.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use primitives::bytes::Bytes;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::U256;
|
||||
use network::{Network, ConsensusParams, BitcoinCashConsensusParams, ConsensusFork};
|
||||
use db::{BlockHeaderProvider, BlockRef};
|
||||
use chain::BlockHeader;
|
||||
use work::work_required;
|
||||
use super::work_required_bitcoin_cash_adjusted;
|
||||
|
||||
#[derive(Default)]
|
||||
struct MemoryBlockHeaderProvider {
|
||||
pub by_height: Vec<BlockHeader>,
|
||||
pub by_hash: HashMap<H256, usize>,
|
||||
}
|
||||
|
||||
impl MemoryBlockHeaderProvider {
|
||||
pub fn insert(&mut self, header: BlockHeader) {
|
||||
self.by_hash.insert(header.hash(), self.by_height.len());
|
||||
self.by_height.push(header);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeaderProvider for MemoryBlockHeaderProvider {
|
||||
fn block_header_bytes(&self, _block_ref: BlockRef) -> Option<Bytes> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn block_header(&self, block_ref: BlockRef) -> Option<BlockHeader> {
|
||||
match block_ref {
|
||||
BlockRef::Hash(ref hash) => self.by_hash.get(hash).map(|h| &self.by_height[*h]).cloned(),
|
||||
BlockRef::Number(height) => self.by_height.get(height as usize).cloned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// original test link:
|
||||
// https://github.com/bitcoinclassic/bitcoinclassic/blob/8bf1fb856df44d1b790b0b835e4c1969be736e25/src/test/pow_tests.cpp#L108
|
||||
#[test]
|
||||
fn bitcoin_cash_req7() {
|
||||
let main_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::NoFork);
|
||||
let uahf_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(BitcoinCashConsensusParams {
|
||||
height: 1000,
|
||||
difficulty_adjustion_time: 0xffffffff,
|
||||
}));
|
||||
let mut header_provider = MemoryBlockHeaderProvider::default();
|
||||
header_provider.insert(BlockHeader {
|
||||
version: 0,
|
||||
previous_header_hash: 0.into(),
|
||||
merkle_root_hash: 0.into(),
|
||||
time: 1269211443,
|
||||
bits: 0x207fffff.into(),
|
||||
nonce: 0,
|
||||
});
|
||||
|
||||
// create x100 pre-HF blocks
|
||||
for height in 1..1000 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 10 * 60;
|
||||
header_provider.insert(header);
|
||||
}
|
||||
|
||||
// create x10 post-HF blocks every 2 hours
|
||||
// MTP still less than 12h
|
||||
for height in 1000..1010 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 60 * 60;
|
||||
header_provider.insert(header.clone());
|
||||
|
||||
let main_bits: u32 = work_required(header.hash(), 0, height as u32, &header_provider, &main_consensus).into();
|
||||
assert_eq!(main_bits, 0x207fffff_u32);
|
||||
let uahf_bits: u32 = work_required(header.hash(), 0, height as u32, &header_provider, &uahf_consensus).into();
|
||||
assert_eq!(uahf_bits, 0x207fffff_u32);
|
||||
}
|
||||
|
||||
// MTP becames greater than 12h
|
||||
let mut header = header_provider.block_header(1009.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 60 * 60;
|
||||
header_provider.insert(header.clone());
|
||||
|
||||
let main_bits: u32 = work_required(header.hash(), 0, 1010, &header_provider, &main_consensus).into();
|
||||
assert_eq!(main_bits, 0x207fffff_u32);
|
||||
let uahf_bits: u32 = work_required(header.hash(), 0, 1010, &header_provider, &uahf_consensus).into();
|
||||
assert_eq!(uahf_bits, 0x1d00ffff_u32);
|
||||
}
|
||||
|
||||
// original test link:
|
||||
// https://github.com/Bitcoin-ABC/bitcoin-abc/blob/d8eac91f8d16716eed0ad11ccac420122280bb13/src/test/pow_tests.cpp#L193
|
||||
#[test]
|
||||
fn bitcoin_cash_adjusted_difficulty() {
|
||||
let uahf_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(BitcoinCashConsensusParams {
|
||||
height: 1000,
|
||||
difficulty_adjustion_time: 0xffffffff,
|
||||
}));
|
||||
|
||||
|
||||
let limit_bits = uahf_consensus.network.max_bits();
|
||||
let initial_bits = limit_bits >> 4;
|
||||
let mut header_provider = MemoryBlockHeaderProvider::default();
|
||||
|
||||
// Genesis block.
|
||||
header_provider.insert(BlockHeader {
|
||||
version: 0,
|
||||
previous_header_hash: 0.into(),
|
||||
merkle_root_hash: 0.into(),
|
||||
time: 1269211443,
|
||||
bits: initial_bits.into(),
|
||||
nonce: 0,
|
||||
});
|
||||
|
||||
// Pile up some blocks every 10 mins to establish some history.
|
||||
for height in 1..2050 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header_provider.insert(header);
|
||||
}
|
||||
|
||||
// Difficulty stays the same as long as we produce a block every 10 mins.
|
||||
let current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2049.into()).unwrap().into(),
|
||||
0, 2050, &header_provider, &uahf_consensus);
|
||||
for height in 2050..2060 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
}
|
||||
|
||||
// Make sure we skip over blocks that are out of wack. To do so, we produce
|
||||
// a block that is far in the future
|
||||
let mut header = header_provider.block_header(2059.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2060.into()).unwrap().into(),
|
||||
0, 2061, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// .. and then produce a block with the expected timestamp.
|
||||
let mut header = header_provider.block_header(2060.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 2 * 600 - 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2061.into()).unwrap().into(),
|
||||
0, 2062, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// The system should continue unaffected by the block with a bogous timestamps.
|
||||
for height in 2062..2082 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 600;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
}
|
||||
|
||||
// We start emitting blocks slightly faster. The first block has no impact.
|
||||
let mut header = header_provider.block_header(2081.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 550;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2082.into()).unwrap().into(),
|
||||
0, 2083, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, current_bits);
|
||||
|
||||
// Now we should see difficulty increase slowly.
|
||||
let mut current_bits = current_bits;
|
||||
for height in 2083..2093 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 550;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < current_work);
|
||||
debug_assert!((current_work - calculated_work) < (current_work >> 10));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0fe7b1.into());
|
||||
|
||||
// If we dramatically shorten block production, difficulty increases faster.
|
||||
for height in 2093..2113 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 10;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < current_work);
|
||||
debug_assert!((current_work - calculated_work) < (current_work >> 4));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0db19f.into());
|
||||
|
||||
// We start to emit blocks significantly slower. The first block has no
|
||||
// impact.
|
||||
let mut header = header_provider.block_header(2112.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let mut current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2113.into()).unwrap().into(),
|
||||
0, 2114, &header_provider, &uahf_consensus);
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c0d9222.into());
|
||||
|
||||
// If we dramatically slow down block production, difficulty decreases.
|
||||
for height in 2114..2207 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work < limit_bits);
|
||||
debug_assert!(calculated_work > current_work);
|
||||
debug_assert!((calculated_work - current_work) < (current_work >> 3));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1c2f13b9.into());
|
||||
|
||||
// Due to the window of time being bounded, next block's difficulty actually
|
||||
// gets harder.
|
||||
let mut header = header_provider.block_header(2206.into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
let mut current_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(2207.into()).unwrap().into(),
|
||||
0, 2208, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(current_bits, 0x1c2ee9bf.into());
|
||||
|
||||
// And goes down again. It takes a while due to the window being bounded and
|
||||
// the skewed block causes 2 blocks to get out of the window.
|
||||
for height in 2208..2400 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
|
||||
let current_work: U256 = current_bits.into();
|
||||
let calculated_work: U256 = calculated_bits.into();
|
||||
debug_assert!(calculated_work <= limit_bits);
|
||||
debug_assert!(calculated_work > current_work);
|
||||
debug_assert!((calculated_work - current_work) < (current_work >> 3));
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
|
||||
// Check the actual value.
|
||||
debug_assert_eq!(current_bits, 0x1d00ffff.into());
|
||||
|
||||
// Once the difficulty reached the minimum allowed level, it doesn't get any
|
||||
// easier.
|
||||
for height in 2400..2405 {
|
||||
let mut header = header_provider.block_header((height - 1).into()).unwrap();
|
||||
header.previous_header_hash = header.hash();
|
||||
header.time = header.time + 6000;
|
||||
header.bits = current_bits;
|
||||
header_provider.insert(header);
|
||||
|
||||
let calculated_bits = work_required_bitcoin_cash_adjusted(header_provider.block_header(height.into()).unwrap().into(),
|
||||
0, height + 1, &header_provider, &uahf_consensus);
|
||||
debug_assert_eq!(calculated_bits, limit_bits.into());
|
||||
|
||||
current_bits = calculated_bits;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue