This commit is contained in:
Svyatoslav Nikolsky 2018-05-22 08:07:34 +03:00
parent 787a36549c
commit 3fc8dab8ee
14 changed files with 276 additions and 48 deletions

View File

@ -43,6 +43,15 @@ pub struct BitcoinCashConsensusParams {
pub monolith_time: u32,
}
#[derive(Debug, Clone)]
/// ZCash consensus parameters.
pub struct ZCashConsensusParams {
pub pow_averaging_window: u32,
pub pow_max_adjust_down: u32,
pub pow_max_adjust_up: u32,
pub pow_target_spacing: u32,
}
#[derive(Debug, Clone)]
/// Concurrent consensus rule forks.
pub enum ConsensusFork {
@ -56,7 +65,7 @@ pub enum ConsensusFork {
/// BUIP-HF Digest for replay protected signature verification across hard forks - https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md
BitcoinCash(BitcoinCashConsensusParams),
/// ZCash.
ZCash,
ZCash(ZCashConsensusParams),
}
impl ConsensusParams {
@ -65,19 +74,19 @@ impl ConsensusParams {
Network::Mainnet | Network::Other(_) => ConsensusParams {
network: network,
bip16_time: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 1333238400, // Apr 1 2012
},
bip34_height: match fork {
ConsensusFork::ZCash => 1,
ConsensusFork::ZCash(_) => 1,
_ => 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
},
bip65_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
},
bip66_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
},
segwit_deployment: match fork {
@ -88,7 +97,7 @@ impl ConsensusParams {
timeout: 1510704000,
activation: Some(481824),
}),
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash => None,
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) => None,
},
fork: fork,
rule_change_activation_threshold: 1916, // 95%
@ -104,19 +113,19 @@ impl ConsensusParams {
Network::Testnet => ConsensusParams {
network: network,
bip16_time: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 1333238400, // Apr 1 2012
},
bip34_height: match fork {
ConsensusFork::ZCash => 1,
ConsensusFork::ZCash(_) => 1,
_ => 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
},
bip65_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
},
bip66_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
},
segwit_deployment: match fork {
@ -127,7 +136,7 @@ impl ConsensusParams {
timeout: 1493596800,
activation: Some(834624),
}),
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash => None,
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) => None,
},
fork: fork,
rule_change_activation_threshold: 1512, // 75%
@ -143,19 +152,19 @@ impl ConsensusParams {
Network::Regtest | Network::Unitest => ConsensusParams {
network: network,
bip16_time: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 1333238400, // Apr 1 2012
},
bip34_height: match fork {
ConsensusFork::ZCash => 1,
ConsensusFork::ZCash(_) => 1,
_ => 100000000, // not activated on regtest
},
bip65_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 1351,
},
bip66_height: match fork {
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
_ => 1251, // used only in rpc tests
},
segwit_deployment: match fork {
@ -166,7 +175,7 @@ impl ConsensusParams {
timeout: ::std::u32::MAX,
activation: None,
}),
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash => None,
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) => None,
},
fork: fork,
rule_change_activation_threshold: 108, // 75%
@ -212,7 +221,7 @@ impl ConsensusFork {
match *self {
ConsensusFork::BitcoinCore => 0,
ConsensusFork::BitcoinCash(ref fork) => fork.height,
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
}
}
@ -227,7 +236,7 @@ impl ConsensusFork {
// size of first fork block must be larger than 1MB
ConsensusFork::BitcoinCash(ref fork) if height == fork.height => 1_000_001,
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) => 0,
ConsensusFork::ZCash => 0,
ConsensusFork::ZCash(_) => 0,
}
}
@ -236,7 +245,7 @@ impl ConsensusFork {
ConsensusFork::BitcoinCash(ref fork) if median_time_past >= fork.monolith_time => 32_000_000,
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => 8_000_000,
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) => 1_000_000,
ConsensusFork::ZCash => 2_000_000,
ConsensusFork::ZCash(_) => 2_000_000,
}
}
@ -245,13 +254,13 @@ impl ConsensusFork {
// according to REQ-5: max_block_sigops = 20000 * ceil((max(blocksize_bytes, 1000000) / 1000000))
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height =>
20_000 * (1 + (block_size - 1) / 1_000_000),
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash => 20_000,
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) => 20_000,
}
}
pub fn max_block_sigops_cost(&self, height: u32, block_size: usize) -> usize {
match *self {
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash =>
ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) =>
self.max_block_sigops(height, block_size) * Self::witness_scale_factor(),
ConsensusFork::BitcoinCore =>
80_000,
@ -260,7 +269,7 @@ impl ConsensusFork {
pub fn max_block_weight(&self, _height: u32) -> usize {
match *self {
ConsensusFork::BitcoinCore | ConsensusFork::ZCash =>
ConsensusFork::BitcoinCore | ConsensusFork::ZCash(_) =>
4_000_000,
ConsensusFork::BitcoinCash(_) =>
unreachable!("BitcoinCash has no SegWit; weight is only checked with SegWit activated; qed"),
@ -290,6 +299,43 @@ impl BitcoinCashConsensusParams {
}
}
impl ZCashConsensusParams {
pub fn new(network: Network) -> Self {
match network {
Network::Mainnet | Network::Other(_) => ZCashConsensusParams {
pow_averaging_window: 17,
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
},
Network::Testnet => ZCashConsensusParams {
pow_averaging_window: 17,
pow_max_adjust_down: 32,
pow_max_adjust_up: 16,
pow_target_spacing: (2.5 * 60.0) as u32,
},
Network::Regtest | Network::Unitest => ZCashConsensusParams {
pow_averaging_window: 17,
pow_max_adjust_down: 0,
pow_max_adjust_up: 0,
pow_target_spacing: (2.5 * 60.0) as u32,
},
}
}
pub fn averaging_window_timespan(&self) -> u32 {
self.pow_averaging_window * self.pow_target_spacing
}
pub fn min_actual_timespan(&self) -> u32 {
(self.averaging_window_timespan() * (100 - self.pow_max_adjust_up)) / 100
}
pub fn max_actual_timespan(&self) -> u32 {
(self.averaging_window_timespan() * (100 + self.pow_max_adjust_down)) / 100
}
}
#[cfg(test)]
mod tests {
use super::super::Network;

View File

@ -11,6 +11,6 @@ mod network;
pub use primitives::{hash, compact};
pub use consensus::{ConsensusParams, ConsensusFork, BitcoinCashConsensusParams};
pub use consensus::{ConsensusParams, ConsensusFork, BitcoinCashConsensusParams, ZCashConsensusParams};
pub use deployments::Deployment;
pub use network::{Magic, Network};

View File

@ -60,9 +60,9 @@ impl Network {
(&ConsensusFork::BitcoinCash(_), Network::Mainnet) => BITCOIN_CASH_MAGIC_MAINNET,
(&ConsensusFork::BitcoinCash(_), Network::Testnet) => BITCOIN_CASH_MAGIC_TESTNET,
(&ConsensusFork::BitcoinCash(_), Network::Regtest) => BITCOIN_CASH_MAGIC_REGTEST,
(&ConsensusFork::ZCash, Network::Mainnet) => ZCASH_MAGIC_MAINNET,
(&ConsensusFork::ZCash, Network::Testnet) => ZCASH_MAGIC_TESTNET,
(&ConsensusFork::ZCash, Network::Regtest) => ZCASH_MAGIC_REGTEST,
(&ConsensusFork::ZCash(_), Network::Mainnet) => ZCASH_MAGIC_MAINNET,
(&ConsensusFork::ZCash(_), Network::Testnet) => ZCASH_MAGIC_TESTNET,
(&ConsensusFork::ZCash(_), Network::Regtest) => ZCASH_MAGIC_REGTEST,
(_, Network::Mainnet) => MAGIC_MAINNET,
(_, Network::Testnet) => MAGIC_TESTNET,
(_, Network::Regtest) => MAGIC_REGTEST,
@ -73,9 +73,9 @@ impl Network {
pub fn max_bits(&self, fork: &ConsensusFork) -> U256 {
match (fork, *self) {
(&ConsensusFork::ZCash, Network::Mainnet) => ZCASH_MAX_BITS_MAINNET.clone(),
(&ConsensusFork::ZCash, Network::Testnet) => ZCASH_MAX_BITS_TESTNET.clone(),
(&ConsensusFork::ZCash, Network::Testnet) => ZCASH_MAX_BITS_REGTEST.clone(),
(&ConsensusFork::ZCash(_), Network::Mainnet) => ZCASH_MAX_BITS_MAINNET.clone(),
(&ConsensusFork::ZCash(_), Network::Testnet) => ZCASH_MAX_BITS_TESTNET.clone(),
(&ConsensusFork::ZCash(_), Network::Testnet) => ZCASH_MAX_BITS_REGTEST.clone(),
(_, Network::Mainnet) | (_, Network::Other(_)) => MAX_BITS_MAINNET.clone(),
(_, Network::Testnet) => MAX_BITS_TESTNET.clone(),
(_, Network::Regtest) => MAX_BITS_REGTEST.clone(),
@ -85,9 +85,9 @@ impl Network {
pub fn port(&self, fork: &ConsensusFork) -> u16 {
match (fork, *self) {
(&ConsensusFork::ZCash, Network::Mainnet) | (&ConsensusFork::ZCash, Network::Other(_)) => 8233,
(&ConsensusFork::ZCash, Network::Testnet) => 18233,
(&ConsensusFork::ZCash, Network::Regtest) | (&ConsensusFork::ZCash, Network::Unitest) => 18344,
(&ConsensusFork::ZCash(_), Network::Mainnet) | (&ConsensusFork::ZCash(_), Network::Other(_)) => 8233,
(&ConsensusFork::ZCash(_), Network::Testnet) => 18233,
(&ConsensusFork::ZCash(_), Network::Regtest) | (&ConsensusFork::ZCash(_), Network::Unitest) => 18344,
(_, Network::Mainnet) | (_, Network::Other(_)) => 8333,
(_, Network::Testnet) => 18333,
(_, Network::Regtest) | (_, Network::Unitest) => 18444,
@ -105,7 +105,7 @@ impl Network {
pub fn genesis_block(&self, fork: &ConsensusFork) -> Block {
match (fork, *self) {
// TODO
(&ConsensusFork::ZCash, Network::Mainnet) | (&ConsensusFork::ZCash, Network::Other(_)) => {
(&ConsensusFork::ZCash(_), Network::Mainnet) | (&ConsensusFork::ZCash(_), Network::Other(_)) => {
use serialization;
use chain;
use chain::hex::FromHex;
@ -114,9 +114,9 @@ impl Network {
let genesis: chain::Block = serialization::deserialize_with_flags(&origin as &[u8], serialization::DESERIALIZE_ZCASH).unwrap();
genesis
},
(&ConsensusFork::ZCash, Network::Testnet) =>
(&ConsensusFork::ZCash(_), Network::Testnet) =>
"".into(),
(&ConsensusFork::ZCash, Network::Regtest) | (&ConsensusFork::ZCash, Network::Unitest) =>
(&ConsensusFork::ZCash(_), Network::Regtest) | (&ConsensusFork::ZCash(_), Network::Unitest) =>
"".into(),
(_, Network::Mainnet) | (_, Network::Other(_)) => "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".into(),

View File

@ -91,7 +91,7 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
let SERIALIZE_ZCASH = 0x80000000; // TODO
let serialization_flags = match cfg.consensus.fork {
ConsensusFork::ZCash => SERIALIZE_ZCASH,
ConsensusFork::ZCash(_) => SERIALIZE_ZCASH,
_ => 0,
};
@ -101,11 +101,11 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
outbound_connections: cfg.outbound_connections,
connection: p2p::NetConfig {
protocol_version: match &cfg.consensus.fork {
&ConsensusFork::ZCash => ZCASH_PROTOCOL_VERSION,
&ConsensusFork::ZCash(_) => ZCASH_PROTOCOL_VERSION,
_ => PROTOCOL_VERSION,
},
protocol_minimum: match &cfg.consensus.fork {
&ConsensusFork::ZCash => ZCASH_PROTOCOL_MINIMUM,
&ConsensusFork::ZCash(_) => ZCASH_PROTOCOL_MINIMUM,
_ => PROTOCOL_MINIMUM,
},
magic: cfg.consensus.magic(),

View File

@ -2,7 +2,7 @@ use std::net;
use clap;
use storage;
use message::Services;
use network::{Network, ConsensusParams, ConsensusFork, BitcoinCashConsensusParams};
use network::{Network, ConsensusParams, ConsensusFork, BitcoinCashConsensusParams, ZCashConsensusParams};
use p2p::InternetProtocol;
use seednodes::{mainnet_seednodes, testnet_seednodes, bitcoin_cash_seednodes,
bitcoin_cash_testnet_seednodes, zcash_seednodes};
@ -63,7 +63,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let consensus = ConsensusParams::new(network, consensus_fork);
match consensus.fork {
ConsensusFork::ZCash => ser::set_default_flags(ser::SERIALIZE_ZCASH),
ConsensusFork::ZCash(_) => ser::set_default_flags(ser::SERIALIZE_ZCASH),
_ => (),
};
@ -81,7 +81,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let user_agent_suffix = match consensus.fork {
ConsensusFork::BitcoinCore => "",
ConsensusFork::BitcoinCash(_) => "/UAHF",
ConsensusFork::ZCash => "",
ConsensusFork::ZCash(_) => "",
};
let user_agent = match network {
Network::Testnet | Network::Mainnet | Network::Unitest | Network::Other(_) => format!("{}{}", USER_AGENT, user_agent_suffix),
@ -106,7 +106,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let seednodes: Vec<String> = match matches.value_of("seednode") {
Some(s) => vec![s.parse().map_err(|_| "Invalid seednode".to_owned())?],
None => match (network, &consensus.fork) {
(Network::Mainnet, &ConsensusFork::ZCash) => zcash_seednodes().into_iter().map(Into::into).collect(),
(Network::Mainnet, &ConsensusFork::ZCash(_)) => zcash_seednodes().into_iter().map(Into::into).collect(),
(Network::Mainnet, &ConsensusFork::BitcoinCash(_)) => bitcoin_cash_seednodes().into_iter().map(Into::into).collect(),
(Network::Testnet, &ConsensusFork::BitcoinCash(_)) => bitcoin_cash_testnet_seednodes().into_iter().map(Into::into).collect(),
(Network::Mainnet, _) => mainnet_seednodes().into_iter().map(Into::into).collect(),
@ -131,7 +131,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
let services = match &consensus.fork {
&ConsensusFork::BitcoinCash(_) => services.with_bitcoin_cash(true),
&ConsensusFork::BitcoinCore => services.with_witness(true),
&ConsensusFork::ZCash => services,
&ConsensusFork::ZCash(_) => services,
};
let verification_level = match matches.value_of("verification-level") {
@ -200,7 +200,7 @@ fn parse_consensus_fork(network: Network, db: &storage::SharedStore, matches: &c
Ok(match new_consensus_fork {
"btc" => ConsensusFork::BitcoinCore,
"bch" => ConsensusFork::BitcoinCash(BitcoinCashConsensusParams::new(network)),
"zcash" => ConsensusFork::ZCash,
"zcash" => ConsensusFork::ZCash(ZCashConsensusParams::new(network)),
_ => unreachable!("hardcoded above"),
})
}

View File

@ -248,6 +248,7 @@ impl<F> BlockHeaderBuilder<F> where F: Invoke<chain::BlockHeader> {
nonce: self.nonce.into(),
merkle_root_hash: self.merkle_root,
version: self.version,
hash_final_sapling_root: None,
equihash_solution: None,
}
)

View File

@ -82,7 +82,7 @@ impl<'a> HeaderEquihashSolution<'a> {
fn check(&self) -> Result<(), Error> {
match self.consensus.fork {
ConsensusFork::ZCash => (),
ConsensusFork::ZCash(_) => (),
_ => return Ok(()),
}

View File

@ -321,7 +321,7 @@ impl<'a> TransactionEval<'a> {
};
let signature_version = match params.fork {
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => SignatureVersion::ForkId,
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash => SignatureVersion::Base,
ConsensusFork::BitcoinCore | ConsensusFork::BitcoinCash(_) | ConsensusFork::ZCash(_) => SignatureVersion::Base,
};
let verify_checksequence = deployments.csv();

View File

@ -294,6 +294,7 @@ mod tests {
time: time,
bits: 0.into(),
nonce: height.into(),
hash_final_sapling_root: None,
equihash_solution: None,
};
previous_header_hash = header.hash();

View File

@ -81,6 +81,7 @@ mod sigops;
mod timestamp;
mod work;
mod work_bch;
mod work_zcash;
// pre-verification
mod verify_block;

View File

@ -23,5 +23,6 @@ pub fn median_timestamp_inclusive(previous_header_hash: H256, store: &BlockHeade
}
timestamps.sort();
println!("=== timestamps: {}..{}", timestamps[0], timestamps[10]);
timestamps[timestamps.len() / 2]
}

View File

@ -6,6 +6,7 @@ use chain::{IndexedBlockHeader, BlockHeader};
use network::{Network, ConsensusParams, ConsensusFork};
use storage::{BlockHeaderProvider, BlockRef};
use work_bch::work_required_bitcoin_cash;
use work_zcash::work_required_zcash;
use constants::{
DOUBLE_SPACING_SECONDS, TARGET_TIMESPAN_SECONDS,
@ -66,6 +67,11 @@ 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");
match consensus.fork {
ConsensusFork::ZCash(ref fork) =>
return work_required_zcash(IndexedBlockHeader {
hash: parent_hash,
raw: parent_header
}, time, height, store, fork, max_bits),
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height =>
return work_required_bitcoin_cash(IndexedBlockHeader {
hash: parent_hash,

View File

@ -172,7 +172,7 @@ fn work_required_bitcoin_cash_adjusted(parent_header: IndexedBlockHeader, time:
}
#[cfg(test)]
mod tests {
pub mod tests {
use std::collections::HashMap;
use primitives::bytes::Bytes;
use primitives::hash::H256;
@ -184,12 +184,16 @@ mod tests {
use super::work_required_bitcoin_cash_adjusted;
#[derive(Default)]
struct MemoryBlockHeaderProvider {
pub struct MemoryBlockHeaderProvider {
pub by_height: Vec<BlockHeader>,
pub by_hash: HashMap<H256, usize>,
}
impl MemoryBlockHeaderProvider {
pub fn last(&self) -> &BlockHeader {
self.by_height.last().unwrap()
}
pub fn insert(&mut self, header: BlockHeader) {
self.by_hash.insert(header.hash(), self.by_height.len());
self.by_height.push(header);
@ -227,6 +231,7 @@ mod tests {
time: 1269211443,
bits: 0x207fffff.into(),
nonce: 0.into(),
hash_final_sapling_root: None,
equihash_solution: None,
});
@ -287,6 +292,7 @@ mod tests {
time: 1269211443,
bits: initial_bits.into(),
nonce: 0.into(),
hash_final_sapling_root: None,
equihash_solution: None,
});

View File

@ -0,0 +1,166 @@
use primitives::compact::Compact;
use primitives::hash::H256;
use primitives::bigint::{Uint, U256};
use chain::{IndexedBlockHeader, BlockHeader};
use network::{Network, ConsensusParams, ZCashConsensusParams};
use storage::BlockHeaderProvider;
use timestamp::median_timestamp_inclusive;
use work::{is_retarget_height, work_required_testnet, work_required_retarget};
/// Returns work required for given header for the ZCash block
pub fn work_required_zcash(parent_header: IndexedBlockHeader, time: u32, height: u32, store: &BlockHeaderProvider, fork: &ZCashConsensusParams, max_bits: Compact) -> Compact {
// Find the first block in the averaging interval
let mut oldest_hash = parent_header.hash.clone();
let mut bits_total: U256 = parent_header.raw.bits.into();
for i in 1..fork.pow_averaging_window {
let block_number = match height.checked_sub(i + 1) {
Some(block_number) => block_number,
None => {
println!("=== XXX");
return max_bits
},
};
let previous_header = store.block_header(block_number.into()).expect("block_number > 0 && block_number < height; qed");
bits_total = bits_total + previous_header.bits.into();
oldest_hash = previous_header.hash();
}
println!("=== bits_total = {:?}", Compact::from_u256(bits_total));
let bits_avg = bits_total / fork.pow_averaging_window.into();
let parent_mtp = median_timestamp_inclusive(parent_header.hash.clone(), store);
let oldest_mtp = median_timestamp_inclusive(oldest_hash, store);
calculate_work_required(bits_avg, parent_mtp, oldest_mtp, fork, max_bits)
}
fn calculate_work_required(bits_avg: U256, parent_mtp: u32, oldest_mtp: u32, fork: &ZCashConsensusParams, max_bits: Compact) -> Compact {
// Limit adjustment step
// Use medians to prevent time-warp attacks
let actual_timespan = parent_mtp - oldest_mtp;
println!("=== parent_mtp: {}", parent_mtp);
println!("=== oldest_mtp: {}", oldest_mtp);
println!("=== actual_timespan_0: {}", actual_timespan);
let mut actual_timespan = fork.averaging_window_timespan() as i64 +
(actual_timespan as i64 - fork.averaging_window_timespan() as i64) / 4;
println!("=== actual_timespan_1: {}", actual_timespan);
if actual_timespan < fork.min_actual_timespan() as i64 {
actual_timespan = fork.min_actual_timespan() as i64;
}
if actual_timespan > fork.max_actual_timespan() as i64 {
actual_timespan = fork.max_actual_timespan() as i64;
}
println!("=== actual_timespan_2: {}", actual_timespan);
// Retarget
let actual_timespan = actual_timespan as u32;
let mut bits_new = bits_avg / fork.averaging_window_timespan().into();
bits_new = bits_new * actual_timespan.into();
if bits_new > max_bits.into() {
return max_bits;
}
bits_new.into()
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use primitives::bytes::Bytes;
use primitives::compact::Compact;
use primitives::hash::H256;
use primitives::bigint::U256;
use network::{Network, ZCashConsensusParams, ConsensusFork};
use storage::{BlockHeaderProvider, BlockRef};
use chain::BlockHeader;
use timestamp::median_timestamp_inclusive;
use work::work_required;
use work_bch::tests::MemoryBlockHeaderProvider;
use super::{work_required_zcash, calculate_work_required};
// original test link:
// https://github.com/Bitcoin-ABC/bitcoin-abc/blob/d8eac91f8d16716eed0ad11ccac420122280bb13/src/test/pow_tests.cpp#L193
#[test]
fn zcash_work_required_works() {
let fork = ZCashConsensusParams::new(Network::Mainnet);
let max_bits = Network::Mainnet.max_bits(&ConsensusFork::ZCash(fork.clone()));
let last_block = 2 * fork.pow_averaging_window;
let first_block = last_block - fork.pow_averaging_window;
// insert genesis block
let mut header_provider = MemoryBlockHeaderProvider::default();
header_provider.insert(BlockHeader {
time: 1269211443,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: 0.into(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
hash_final_sapling_root: None,
equihash_solution: None,
});
// Start with blocks evenly-spaced and equal difficulty
for i in 1..last_block+1 {
let header = BlockHeader {
time: header_provider.last().time + fork.pow_target_spacing,
bits: Compact::new(0x1e7fffff),
version: 0,
previous_header_hash: 0.into(),
merkle_root_hash: 0.into(),
nonce: 0.into(),
hash_final_sapling_root: None,
equihash_solution: None,
};
header_provider.insert(header);
}
// Result should be unchanged, modulo integer division precision loss
let mut expected: U256 = Compact::new(0x1e7fffff).into();
expected = expected / fork.averaging_window_timespan().into();
expected = expected * fork.averaging_window_timespan().into();
let actual = work_required_zcash(header_provider.last().clone().into(),
0, header_provider.by_height.len() as u32, &header_provider, &fork, max_bits.into());
assert_eq!(actual, expected.into());
// Result should be the same as if last difficulty was used
let bits_avg: U256 = header_provider.by_height[last_block as usize].bits.into();
let expected = calculate_work_required(bits_avg,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&fork, max_bits.into());
let actual = work_required_zcash(header_provider.last().clone().into(),
0, header_provider.by_height.len() as u32, &header_provider, &fork, max_bits.into());
assert_eq!(actual, expected);
/*
// Result should be unchanged, modulo integer division precision loss
let mut bits_expected: U256 = Compact::new(0x1e7fffff).into();
bits_expected = bits_expected / fork.averaging_window_timespan().into();
bits_expected = bits_expected * fork.averaging_window_timespan().into();
assert_eq!(calculate_work_required(bits_expected,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&fork, max_bits.into()), bits_expected.into());
// Randomise the final block time (plus 1 to ensure it is always different)
use std::rand::{task_rng, Rng};
header_provider.by_height[last_block].time += task_rng().gen_range(1, fork.pow_target_spacing / 2);
// Result should be the same as if last difficulty was used
bits_expected = header_provider.by_height[last_block].bits;
assert_eq!(calculate_work_required(bits_expected,
median_timestamp_inclusive(header_provider.by_height[last_block as usize].hash(), &header_provider),
median_timestamp_inclusive(header_provider.by_height[first_block as usize].hash(), &header_provider),
&fork, max_bits.into()), bits_expected.into());
// Result should be the same as if last difficulty was used
bnAvg.SetCompact(blocks[lastBlk].nBits);
EXPECT_EQ(CalculateNextWorkRequired(bnAvg,
blocks[lastBlk].GetMedianTimePast(),
blocks[firstBlk].GetMedianTimePast(),
params),
GetNextWorkRequired(&blocks[lastBlk], nullptr, params));
// Result should not be unchanged
EXPECT_NE(0x1e7fffff, GetNextWorkRequired(&blocks[lastBlk], nullptr, params));*/
}
}