BitcoinCash: difficulty adjustment fork
This commit is contained in:
parent
77eb2dec67
commit
261bd518fd
|
@ -653,8 +653,8 @@ name = "network"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chain 0.1.0",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"primitives 0.1.0",
|
||||
"serialization 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -69,7 +69,7 @@ mod tests {
|
|||
fn test_message_header_serialization() {
|
||||
let expected = "f9beb4d96164647200000000000000001f000000ed52399b".into();
|
||||
let header = MessageHeader {
|
||||
magic: Network::Mainnet.magic(ConsensusFork::NoFork),
|
||||
magic: Network::Mainnet.magic(&ConsensusFork::NoFork),
|
||||
command: "addr".into(),
|
||||
len: 0x1f,
|
||||
checksum: "ed52399b".into(),
|
||||
|
@ -82,12 +82,12 @@ mod tests {
|
|||
fn test_message_header_deserialization() {
|
||||
let raw: Bytes = "f9beb4d96164647200000000000000001f000000ed52399b".into();
|
||||
let expected = MessageHeader {
|
||||
magic: Network::Mainnet.magic(ConsensusFork::NoFork),
|
||||
magic: Network::Mainnet.magic(&ConsensusFork::NoFork),
|
||||
command: "addr".into(),
|
||||
len: 0x1f,
|
||||
checksum: "ed52399b".into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, MessageHeader::deserialize(&raw, Network::Mainnet.magic(ConsensusFork::NoFork)).unwrap());
|
||||
assert_eq!(expected, MessageHeader::deserialize(&raw, Network::Mainnet.magic(&ConsensusFork::NoFork)).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
serialization = { path = "../serialization" }
|
||||
lazy_static = "0.2"
|
||||
chain = { path = "../chain" }
|
||||
primitives = { path = "../primitives" }
|
||||
|
|
|
@ -2,9 +2,9 @@ use hash::H256;
|
|||
use {Network, Magic, Deployment};
|
||||
|
||||
/// First block of SegWit2x fork.
|
||||
pub const SEGWIT2X_FORK_BLOCK: u32 = 494784; // https://segwit2x.github.io/segwit2x-announce.html
|
||||
const SEGWIT2X_FORK_BLOCK: u32 = 494784; // https://segwit2x.github.io/segwit2x-announce.html
|
||||
/// First block of BitcoinCash fork.
|
||||
pub const BITCOIN_CASH_FORK_BLOCK: u32 = 478559; // https://blockchair.com/bitcoin-cash/block/478559
|
||||
const BITCOIN_CASH_FORK_BLOCK: u32 = 478559; // https://blockchair.com/bitcoin-cash/block/478559
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Parameters that influence chain consensus.
|
||||
|
@ -35,7 +35,25 @@ pub struct ConsensusParams {
|
|||
pub segwit_deployment: Option<Deployment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
/// Bitcoin cash consensus parameters.
|
||||
pub struct BitcoinCashConsensusParams {
|
||||
/// Initial BCH hard fork height.
|
||||
pub height: u32,
|
||||
/// Time when difficulty adjustment hardfork becomes active (~Nov 13 2017).
|
||||
/// https://reviews.bitcoinabc.org/D601
|
||||
// TODO: change to height after activation
|
||||
pub difficulty_adjustion_time: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// SegWit2x consensus parameters.
|
||||
pub struct SegWit2xConsensusParams {
|
||||
/// Initial SegWit2x hard fork height.
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Concurrent consensus rule forks.
|
||||
pub enum ConsensusFork {
|
||||
/// No fork.
|
||||
|
@ -47,14 +65,14 @@ pub enum ConsensusFork {
|
|||
/// Segregated Witness (Consensus layer) - https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
|
||||
/// Block size increase to 2MB - https://github.com/bitcoin/bips/blob/master/bip-0102.mediawiki
|
||||
/// Readiness checklist - https://segwit2x.github.io/segwit2x-announce.html
|
||||
SegWit2x(u32),
|
||||
SegWit2x(SegWit2xConsensusParams),
|
||||
/// Bitcoin Cash (aka UAHF).
|
||||
/// `u32` is height of the first block, for which new consensus rules are applied.
|
||||
/// Briefly: no SegWit + blocks up to 8MB + replay protection.
|
||||
/// Technical specification:
|
||||
/// UAHF Technical Specification - https://github.com/Bitcoin-UAHF/spec/blob/master/uahf-technical-spec.md
|
||||
/// BUIP-HF Digest for replay protected signature verification across hard forks - https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md
|
||||
BitcoinCash(u32),
|
||||
BitcoinCash(BitcoinCashConsensusParams),
|
||||
}
|
||||
|
||||
impl ConsensusParams {
|
||||
|
@ -66,16 +84,6 @@ impl ConsensusParams {
|
|||
bip34_height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
|
||||
bip65_height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
|
||||
bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 1916, // 95%
|
||||
miner_confirmation_window: 2016,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 1462060800,
|
||||
timeout: 1493596800,
|
||||
activation: Some(419328),
|
||||
}),
|
||||
segwit_deployment: match fork {
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) => Some(Deployment {
|
||||
name: "segwit",
|
||||
|
@ -86,6 +94,16 @@ impl ConsensusParams {
|
|||
}),
|
||||
ConsensusFork::BitcoinCash(_) => None,
|
||||
},
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 1916, // 95%
|
||||
miner_confirmation_window: 2016,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 1462060800,
|
||||
timeout: 1493596800,
|
||||
activation: Some(419328),
|
||||
}),
|
||||
},
|
||||
Network::Testnet => ConsensusParams {
|
||||
network: network,
|
||||
|
@ -93,16 +111,6 @@ impl ConsensusParams {
|
|||
bip34_height: 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
|
||||
bip65_height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
|
||||
bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 1512, // 75%
|
||||
miner_confirmation_window: 2016,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 1456790400,
|
||||
timeout: 1493596800,
|
||||
activation: Some(770112),
|
||||
}),
|
||||
segwit_deployment: match fork {
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) => Some(Deployment {
|
||||
name: "segwit",
|
||||
|
@ -113,6 +121,16 @@ impl ConsensusParams {
|
|||
}),
|
||||
ConsensusFork::BitcoinCash(_) => None,
|
||||
},
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 1512, // 75%
|
||||
miner_confirmation_window: 2016,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 1456790400,
|
||||
timeout: 1493596800,
|
||||
activation: Some(770112),
|
||||
}),
|
||||
},
|
||||
Network::Regtest | Network::Unitest => ConsensusParams {
|
||||
network: network,
|
||||
|
@ -120,16 +138,6 @@ impl ConsensusParams {
|
|||
bip34_height: 100000000, // not activated on regtest
|
||||
bip65_height: 1351,
|
||||
bip66_height: 1251, // used only in rpc tests
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 108, // 75%
|
||||
miner_confirmation_window: 144,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 0,
|
||||
timeout: 0,
|
||||
activation: Some(0),
|
||||
}),
|
||||
segwit_deployment: match fork {
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) => Some(Deployment {
|
||||
name: "segwit",
|
||||
|
@ -140,12 +148,22 @@ impl ConsensusParams {
|
|||
}),
|
||||
ConsensusFork::BitcoinCash(_) => None,
|
||||
},
|
||||
fork: fork,
|
||||
rule_change_activation_threshold: 108, // 75%
|
||||
miner_confirmation_window: 144,
|
||||
csv_deployment: Some(Deployment {
|
||||
name: "csv",
|
||||
bit: 0,
|
||||
start_time: 0,
|
||||
timeout: 0,
|
||||
activation: Some(0),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn magic(&self) -> Magic {
|
||||
self.network.magic(self.fork)
|
||||
self.network.magic(&self.fork)
|
||||
}
|
||||
|
||||
pub fn is_bip30_exception(&self, hash: &H256, height: u32) -> bool {
|
||||
|
@ -170,6 +188,14 @@ impl ConsensusFork {
|
|||
4
|
||||
}
|
||||
|
||||
pub fn activation_height(&self) -> u32 {
|
||||
match *self {
|
||||
ConsensusFork::NoFork => 0,
|
||||
ConsensusFork::SegWit2x(ref fork) => fork.height,
|
||||
ConsensusFork::BitcoinCash(ref fork) => fork.height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_transaction_size(&self) -> usize {
|
||||
// BitcoinCash: according to REQ-5: max size of tx is still 1_000_000
|
||||
// SegWit: size * 4 <= 4_000_000 ===> max size of tx is still 1_000_000
|
||||
|
@ -179,15 +205,15 @@ impl ConsensusFork {
|
|||
pub fn min_block_size(&self, height: u32) -> usize {
|
||||
match *self {
|
||||
// size of first fork block must be larger than 1MB
|
||||
ConsensusFork::BitcoinCash(fork_height) if height == fork_height => 1_000_001,
|
||||
ConsensusFork::BitcoinCash(ref fork) if height == fork.height => 1_000_001,
|
||||
ConsensusFork::NoFork | ConsensusFork::BitcoinCash(_) | ConsensusFork::SegWit2x(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_block_size(&self, height: u32) -> usize {
|
||||
match *self {
|
||||
ConsensusFork::SegWit2x(fork_height) if height >= fork_height => 2_000_000,
|
||||
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height => 8_000_000,
|
||||
ConsensusFork::SegWit2x(ref fork) if height >= fork.height => 2_000_000,
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => 8_000_000,
|
||||
ConsensusFork::NoFork | ConsensusFork::BitcoinCash(_) | ConsensusFork::SegWit2x(_) => 1_000_000,
|
||||
}
|
||||
}
|
||||
|
@ -195,9 +221,9 @@ impl ConsensusFork {
|
|||
pub fn max_block_sigops(&self, height: u32, block_size: usize) -> usize {
|
||||
match *self {
|
||||
// according to REQ-5: max_block_sigops = 20000 * ceil((max(blocksize_bytes, 1000000) / 1000000))
|
||||
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height =>
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height =>
|
||||
20_000 * (1 + (block_size - 1) / 1_000_000),
|
||||
ConsensusFork::SegWit2x(fork_height) if height >= fork_height =>
|
||||
ConsensusFork::SegWit2x(ref fork) if height >= fork.height =>
|
||||
40_000,
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) | ConsensusFork::BitcoinCash(_) => 20_000,
|
||||
}
|
||||
|
@ -207,7 +233,7 @@ impl ConsensusFork {
|
|||
match *self {
|
||||
ConsensusFork::BitcoinCash(_) =>
|
||||
self.max_block_sigops(height, block_size) * Self::witness_scale_factor(),
|
||||
ConsensusFork::SegWit2x(fork_height) if height >= fork_height =>
|
||||
ConsensusFork::SegWit2x(ref fork) if height >= fork.height =>
|
||||
160_000,
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) =>
|
||||
80_000,
|
||||
|
@ -216,7 +242,7 @@ impl ConsensusFork {
|
|||
|
||||
pub fn max_block_weight(&self, height: u32) -> usize {
|
||||
match *self {
|
||||
ConsensusFork::SegWit2x(fork_height) if height >= fork_height =>
|
||||
ConsensusFork::SegWit2x(ref fork) if height >= fork.height =>
|
||||
8_000_000,
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) =>
|
||||
4_000_000,
|
||||
|
@ -226,6 +252,23 @@ impl ConsensusFork {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for BitcoinCashConsensusParams {
|
||||
fn default() -> Self {
|
||||
BitcoinCashConsensusParams {
|
||||
height: BITCOIN_CASH_FORK_BLOCK,
|
||||
difficulty_adjustion_time: 1510600000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SegWit2xConsensusParams {
|
||||
fn default() -> Self {
|
||||
SegWit2xConsensusParams {
|
||||
height: SEGWIT2X_FORK_BLOCK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::Network;
|
||||
|
@ -269,27 +312,31 @@ mod tests {
|
|||
#[test]
|
||||
fn test_consensus_fork_min_block_size() {
|
||||
assert_eq!(ConsensusFork::NoFork.min_block_size(0), 0);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).min_block_size(0), 0);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).min_block_size(100), 0);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).min_block_size(0), 0);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).min_block_size(100), 1_000_001);
|
||||
let fork = ConsensusFork::SegWit2x(Default::default());
|
||||
assert_eq!(fork.min_block_size(0), 0);
|
||||
assert_eq!(fork.min_block_size(fork.activation_height()), 0);
|
||||
let fork = ConsensusFork::BitcoinCash(Default::default());
|
||||
assert_eq!(fork.min_block_size(0), 0);
|
||||
assert_eq!(fork.min_block_size(fork.activation_height()), 1_000_001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consensus_fork_max_transaction_size() {
|
||||
assert_eq!(ConsensusFork::NoFork.max_transaction_size(), 1_000_000);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).max_transaction_size(), 1_000_000);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).max_transaction_size(), 1_000_000);
|
||||
assert_eq!(ConsensusFork::SegWit2x(Default::default()).max_transaction_size(), 1_000_000);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(Default::default()).max_transaction_size(), 1_000_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consensus_fork_max_block_sigops() {
|
||||
assert_eq!(ConsensusFork::NoFork.max_block_sigops(0, 1_000_000), 20_000);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(0, 1_000_000), 20_000);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(100, 2_000_000), 40_000);
|
||||
assert_eq!(ConsensusFork::SegWit2x(100).max_block_sigops(200, 3_000_000), 40_000);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(0, 1_000_000), 20_000);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(100, 2_000_000), 40_000);
|
||||
assert_eq!(ConsensusFork::BitcoinCash(100).max_block_sigops(200, 3_000_000), 60_000);
|
||||
let fork = ConsensusFork::SegWit2x(Default::default());
|
||||
assert_eq!(fork.max_block_sigops(0, 1_000_000), 20_000);
|
||||
assert_eq!(fork.max_block_sigops(fork.activation_height(), 2_000_000), 40_000);
|
||||
assert_eq!(fork.max_block_sigops(fork.activation_height() + 100, 3_000_000), 40_000);
|
||||
let fork = ConsensusFork::BitcoinCash(Default::default());
|
||||
assert_eq!(fork.max_block_sigops(0, 1_000_000), 20_000);
|
||||
assert_eq!(fork.max_block_sigops(fork.activation_height(), 2_000_000), 40_000);
|
||||
assert_eq!(fork.max_block_sigops(fork.activation_height() + 100, 3_000_000), 60_000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate chain;
|
||||
extern crate primitives;
|
||||
extern crate serialization as ser;
|
||||
|
||||
mod consensus;
|
||||
mod deployments;
|
||||
|
@ -8,6 +10,6 @@ mod network;
|
|||
|
||||
pub use primitives::{hash, compact};
|
||||
|
||||
pub use consensus::{ConsensusParams, ConsensusFork, SEGWIT2X_FORK_BLOCK, BITCOIN_CASH_FORK_BLOCK};
|
||||
pub use consensus::{ConsensusParams, ConsensusFork, BitcoinCashConsensusParams, SegWit2xConsensusParams};
|
||||
pub use deployments::Deployment;
|
||||
pub use network::{Magic, Network};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use compact::Compact;
|
||||
use chain::Block;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::U256;
|
||||
use {ConsensusFork};
|
||||
|
||||
const MAGIC_MAINNET: u32 = 0xD9B4BEF9;
|
||||
|
@ -15,9 +16,14 @@ const BITCOIN_CASH_MAGIC_MAINNET: u32 = 0xE8F3E1E3;
|
|||
const BITCOIN_CASH_MAGIC_TESTNET: u32 = 0xF4F3E5F4;
|
||||
const BITCOIN_CASH_MAGIC_REGTEST: u32 = 0xFABFB5DA;
|
||||
|
||||
const MAX_BITS_MAINNET: u32 = 0x1d00ffff;
|
||||
const MAX_BITS_TESTNET: u32 = 0x1d00ffff;
|
||||
const MAX_BITS_REGTEST: u32 = 0x207fffff;
|
||||
lazy_static! {
|
||||
static ref MAX_BITS_MAINNET: U256 = "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()
|
||||
.expect("hardcoded value should parse without errors");
|
||||
static ref MAX_BITS_TESTNET: U256 = "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()
|
||||
.expect("hardcoded value should parse without errors");
|
||||
static ref MAX_BITS_REGTEST: U256 = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()
|
||||
.expect("hardcoded value should parse without errors");
|
||||
}
|
||||
|
||||
/// Network magic type.
|
||||
pub type Magic = u32;
|
||||
|
@ -38,11 +44,11 @@ pub enum Network {
|
|||
}
|
||||
|
||||
impl Network {
|
||||
pub fn magic(&self, fork: ConsensusFork) -> Magic {
|
||||
pub fn magic(&self, fork: &ConsensusFork) -> Magic {
|
||||
match (fork, *self) {
|
||||
(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::BitcoinCash(_), Network::Mainnet) => BITCOIN_CASH_MAGIC_MAINNET,
|
||||
(&ConsensusFork::BitcoinCash(_), Network::Testnet) => BITCOIN_CASH_MAGIC_TESTNET,
|
||||
(&ConsensusFork::BitcoinCash(_), Network::Regtest) => BITCOIN_CASH_MAGIC_REGTEST,
|
||||
(_, Network::Mainnet) => MAGIC_MAINNET,
|
||||
(_, Network::Testnet) => MAGIC_TESTNET,
|
||||
(_, Network::Regtest) => MAGIC_REGTEST,
|
||||
|
@ -51,12 +57,12 @@ impl Network {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn max_bits(&self) -> Compact {
|
||||
pub fn max_bits(&self) -> U256 {
|
||||
match *self {
|
||||
Network::Mainnet | Network::Other(_) => MAX_BITS_MAINNET.into(),
|
||||
Network::Testnet => MAX_BITS_TESTNET.into(),
|
||||
Network::Regtest => MAX_BITS_REGTEST.into(),
|
||||
Network::Unitest => Compact::max_value(),
|
||||
Network::Mainnet | Network::Other(_) => MAX_BITS_MAINNET.clone(),
|
||||
Network::Testnet => MAX_BITS_TESTNET.clone(),
|
||||
Network::Regtest => MAX_BITS_REGTEST.clone(),
|
||||
Network::Unitest => Compact::max_value().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,18 +110,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_network_magic_number() {
|
||||
assert_eq!(MAGIC_MAINNET, Network::Mainnet.magic(ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_TESTNET, Network::Testnet.magic(ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_REGTEST, Network::Regtest.magic(ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_UNITEST, Network::Unitest.magic(ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_MAINNET, Network::Mainnet.magic(&ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_TESTNET, Network::Testnet.magic(&ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_REGTEST, Network::Regtest.magic(&ConsensusFork::NoFork));
|
||||
assert_eq!(MAGIC_UNITEST, Network::Unitest.magic(&ConsensusFork::NoFork));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_max_bits() {
|
||||
assert_eq!(Network::Mainnet.max_bits(), MAX_BITS_MAINNET.into());
|
||||
assert_eq!(Network::Testnet.max_bits(), MAX_BITS_TESTNET.into());
|
||||
assert_eq!(Network::Regtest.max_bits(), MAX_BITS_REGTEST.into());
|
||||
assert_eq!(Network::Unitest.max_bits(), Compact::max_value());
|
||||
assert_eq!(Network::Mainnet.max_bits(), *MAX_BITS_MAINNET);
|
||||
assert_eq!(Network::Testnet.max_bits(), *MAX_BITS_TESTNET);
|
||||
assert_eq!(Network::Regtest.max_bits(), *MAX_BITS_REGTEST);
|
||||
assert_eq!(Network::Unitest.max_bits(), Compact::max_value().into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -287,7 +287,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_handshake() {
|
||||
let magic = Network::Mainnet.magic(ConsensusFork::NoFork);
|
||||
let magic = Network::Mainnet.magic(&ConsensusFork::NoFork);
|
||||
let version = 70012;
|
||||
let local_version = local_version();
|
||||
let remote_version = remote_version();
|
||||
|
@ -317,7 +317,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_accept_handshake() {
|
||||
let magic = Network::Mainnet.magic(ConsensusFork::NoFork);
|
||||
let magic = Network::Mainnet.magic(&ConsensusFork::NoFork);
|
||||
let version = 70012;
|
||||
let local_version = local_version();
|
||||
let remote_version = remote_version();
|
||||
|
@ -346,7 +346,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_self_handshake() {
|
||||
let magic = Network::Mainnet.magic(ConsensusFork::NoFork);
|
||||
let magic = Network::Mainnet.magic(&ConsensusFork::NoFork);
|
||||
let version = 70012;
|
||||
let remote_version = local_version();
|
||||
let local_version = local_version();
|
||||
|
@ -367,7 +367,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_accept_self_handshake() {
|
||||
let magic = Network::Mainnet.magic(ConsensusFork::NoFork);
|
||||
let magic = Network::Mainnet.magic(&ConsensusFork::NoFork);
|
||||
let version = 70012;
|
||||
let remote_version = local_version();
|
||||
let local_version = local_version();
|
||||
|
@ -388,8 +388,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_fails_to_accept_other_fork_node() {
|
||||
let magic1 = Network::Mainnet.magic(ConsensusFork::NoFork);
|
||||
let magic2 = Network::Mainnet.magic(ConsensusFork::BitcoinCash(0));
|
||||
let magic1 = Network::Mainnet.magic(&ConsensusFork::NoFork);
|
||||
let magic2 = Network::Mainnet.magic(&ConsensusFork::BitcoinCash(Default::default()));
|
||||
let version = 70012;
|
||||
let local_version = local_version();
|
||||
let remote_version = remote_version();
|
||||
|
|
|
@ -74,20 +74,20 @@ mod tests {
|
|||
let nonce = "5845303b6da97786".into();
|
||||
let expected = (name, nonce);
|
||||
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork)).wait().unwrap(), Ok(expected));
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Testnet.magic(ConsensusFork::NoFork)).wait().unwrap(), Err(Error::InvalidMagic));
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork)).wait().unwrap(), Ok(expected));
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Testnet.magic(&ConsensusFork::NoFork)).wait().unwrap(), Err(Error::InvalidMagic));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_too_short_any_message() {
|
||||
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c00c765845303b6da977".into();
|
||||
assert!(read_any_message(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork)).wait().is_err());
|
||||
assert!(read_any_message(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork)).wait().is_err());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_read_any_message_with_invalid_checksum() {
|
||||
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c01c765845303b6da97786".into();
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork)).wait().unwrap(), Err(Error::InvalidChecksum));
|
||||
assert_eq!(read_any_message(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork)).wait().unwrap(), Err(Error::InvalidChecksum));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,25 +40,25 @@ mod tests {
|
|||
fn test_read_header() {
|
||||
let raw: Bytes = "f9beb4d96164647200000000000000001f000000ed52399b".into();
|
||||
let expected = MessageHeader {
|
||||
magic: Network::Mainnet.magic(ConsensusFork::NoFork),
|
||||
magic: Network::Mainnet.magic(&ConsensusFork::NoFork),
|
||||
command: "addr".into(),
|
||||
len: 0x1f,
|
||||
checksum: "ed52399b".into(),
|
||||
};
|
||||
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork)).wait().unwrap().1, Ok(expected));
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Testnet.magic(ConsensusFork::NoFork)).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork)).wait().unwrap().1, Ok(expected));
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Testnet.magic(&ConsensusFork::NoFork)).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_header_with_invalid_magic() {
|
||||
let raw: Bytes = "f9beb4d86164647200000000000000001f000000ed52399b".into();
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Testnet.magic(ConsensusFork::NoFork)).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
assert_eq!(read_header(raw.as_ref(), Network::Testnet.magic(&ConsensusFork::NoFork)).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_too_short_header() {
|
||||
let raw: Bytes = "f9beb4d96164647200000000000000001f000000ed5239".into();
|
||||
assert!(read_header(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork)).wait().is_err());
|
||||
assert!(read_header(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork)).wait().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,21 +78,21 @@ mod tests {
|
|||
fn test_read_message() {
|
||||
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c00c765845303b6da97786".into();
|
||||
let ping = Ping::new(u64::from_str_radix("8677a96d3b304558", 16).unwrap());
|
||||
assert_eq!(read_message(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork), 0).wait().unwrap().1, Ok(ping));
|
||||
assert_eq!(read_message::<Ping, _>(raw.as_ref(), Network::Testnet.magic(ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
assert_eq!(read_message::<Pong, _>(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidCommand));
|
||||
assert_eq!(read_message(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork), 0).wait().unwrap().1, Ok(ping));
|
||||
assert_eq!(read_message::<Ping, _>(raw.as_ref(), Network::Testnet.magic(&ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidMagic));
|
||||
assert_eq!(read_message::<Pong, _>(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidCommand));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_too_short_message() {
|
||||
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c00c765845303b6da977".into();
|
||||
assert!(read_message::<Ping, _>(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork), 0).wait().is_err());
|
||||
assert!(read_message::<Ping, _>(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork), 0).wait().is_err());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_read_message_with_invalid_checksum() {
|
||||
let raw: Bytes = "f9beb4d970696e6700000000000000000800000083c01c765845303b6da97786".into();
|
||||
assert_eq!(read_message::<Ping, _>(raw.as_ref(), Network::Mainnet.magic(ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidChecksum));
|
||||
assert_eq!(read_message::<Ping, _>(raw.as_ref(), Network::Mainnet.magic(&ConsensusFork::NoFork), 0).wait().unwrap().1, Err(Error::InvalidChecksum));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::net;
|
|||
use clap;
|
||||
use db;
|
||||
use message::Services;
|
||||
use network::{Network, ConsensusParams, ConsensusFork, SEGWIT2X_FORK_BLOCK, BITCOIN_CASH_FORK_BLOCK};
|
||||
use network::{Network, ConsensusParams, ConsensusFork};
|
||||
use p2p::InternetProtocol;
|
||||
use seednodes::{mainnet_seednodes, testnet_seednodes, segwit2x_seednodes, bitcoin_cash_seednodes, bitcoin_cash_testnet_seednodes};
|
||||
use rpc_apis::ApiSet;
|
||||
|
@ -71,7 +71,7 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
|
|||
};
|
||||
|
||||
// to skip idiotic 30 seconds delay in test-scripts
|
||||
let user_agent_suffix = match consensus_fork {
|
||||
let user_agent_suffix = match consensus.fork {
|
||||
ConsensusFork::NoFork => "",
|
||||
ConsensusFork::SegWit2x(_) => "/SegWit2x",
|
||||
ConsensusFork::BitcoinCash(_) => "/UAHF",
|
||||
|
@ -98,15 +98,15 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
|
|||
|
||||
let mut 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::BitcoinCash(_)) => bitcoin_cash_seednodes().into_iter().map(Into::into).collect(),
|
||||
(Network::Testnet, ConsensusFork::BitcoinCash(_)) => bitcoin_cash_testnet_seednodes().into_iter().map(Into::into).collect(),
|
||||
None => match (network, &consensus.fork) {
|
||||
(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(),
|
||||
(Network::Testnet, _) => testnet_seednodes().into_iter().map(Into::into).collect(),
|
||||
(Network::Other(_), _) | (Network::Regtest, _) | (Network::Unitest, _) => Vec::new(),
|
||||
},
|
||||
};
|
||||
match consensus_fork {
|
||||
match consensus.fork {
|
||||
ConsensusFork::SegWit2x(_) => seednodes.extend(segwit2x_seednodes().into_iter().map(Into::into)),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -124,9 +124,9 @@ pub fn parse(matches: &clap::ArgMatches) -> Result<Config, String> {
|
|||
};
|
||||
|
||||
let services = Services::default().with_network(true);
|
||||
let services = match consensus.fork {
|
||||
ConsensusFork::BitcoinCash(_) => services.with_bitcoin_cash(true),
|
||||
ConsensusFork::NoFork | ConsensusFork::SegWit2x(_) => services.with_witness(true),
|
||||
let services = match &consensus.fork {
|
||||
&ConsensusFork::BitcoinCash(_) => services.with_bitcoin_cash(true),
|
||||
&ConsensusFork::NoFork | &ConsensusFork::SegWit2x(_) => services.with_witness(true),
|
||||
};
|
||||
|
||||
let verification_level = match matches.value_of("verification-level") {
|
||||
|
@ -194,8 +194,8 @@ fn parse_consensus_fork(db: &db::SharedStore, matches: &clap::ArgMatches) -> Res
|
|||
|
||||
Ok(match new_consensus_fork {
|
||||
"segwit" => ConsensusFork::NoFork,
|
||||
"segwit2x" => ConsensusFork::SegWit2x(SEGWIT2X_FORK_BLOCK),
|
||||
"bitcoin-cash" => ConsensusFork::BitcoinCash(BITCOIN_CASH_FORK_BLOCK),
|
||||
"segwit2x" => ConsensusFork::SegWit2x(Default::default()),
|
||||
"bitcoin-cash" => ConsensusFork::BitcoinCash(Default::default()),
|
||||
_ => unreachable!("hardcoded above"),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -382,7 +382,7 @@ pub mod tests {
|
|||
.build()
|
||||
.merkled_header()
|
||||
.parent(rolling_hash.clone())
|
||||
.bits(Network::Unitest.max_bits())
|
||||
.bits(Network::Unitest.max_bits().into())
|
||||
.build()
|
||||
.build();
|
||||
rolling_hash = next_block.hash();
|
||||
|
@ -401,7 +401,7 @@ pub mod tests {
|
|||
.build()
|
||||
.merkled_header()
|
||||
.parent(last_block_hash)
|
||||
.bits(Network::Unitest.max_bits())
|
||||
.bits(Network::Unitest.max_bits().into())
|
||||
.build()
|
||||
.build().into();
|
||||
|
||||
|
|
|
@ -306,13 +306,13 @@ impl<'a> TransactionEval<'a> {
|
|||
) -> Self {
|
||||
let verify_p2sh = time >= params.bip16_time;
|
||||
let verify_strictenc = match params.fork {
|
||||
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height => true,
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => true,
|
||||
_ => false,
|
||||
};
|
||||
let verify_locktime = height >= params.bip65_height;
|
||||
let verify_dersig = height >= params.bip66_height;
|
||||
let signature_version = match params.fork {
|
||||
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height => SignatureVersion::ForkId,
|
||||
ConsensusFork::BitcoinCash(ref fork) if height >= fork.height => SignatureVersion::ForkId,
|
||||
ConsensusFork::NoFork | ConsensusFork::BitcoinCash(_) | ConsensusFork::SegWit2x(_) => SignatureVersion::Base,
|
||||
};
|
||||
|
||||
|
@ -429,9 +429,9 @@ impl<'a> TransactionReturnReplayProtection<'a> {
|
|||
}
|
||||
|
||||
fn check(&self) -> Result<(), TransactionError> {
|
||||
if let ConsensusFork::BitcoinCash(fork_block) = self.consensus.fork {
|
||||
if let ConsensusFork::BitcoinCash(ref fork) = self.consensus.fork {
|
||||
// Transactions with such OP_RETURNs shall be considered valid again for block 530,001 and onwards
|
||||
if self.height >= fork_block && self.height <= 530_000 {
|
||||
if self.height >= fork.height && self.height <= 530_000 {
|
||||
if (*self.transaction).raw.outputs.iter()
|
||||
.any(|out| out.script_pubkey == *BITCOIN_CASH_RETURN_REPLAY_PROTECTION_SCRIPT) {
|
||||
return Err(TransactionError::ReturnReplayProtection)
|
||||
|
@ -492,10 +492,10 @@ mod tests {
|
|||
|
||||
assert_eq!(transaction.raw.outputs[0].script_pubkey.len(), 46 + 2);
|
||||
|
||||
let consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(100));
|
||||
let checker = TransactionReturnReplayProtection::new(CanonTransaction::new(&transaction), &consensus, 100);
|
||||
let consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(Default::default()));
|
||||
let checker = TransactionReturnReplayProtection::new(CanonTransaction::new(&transaction), &consensus, consensus.fork.activation_height());
|
||||
assert_eq!(checker.check(), Err(TransactionError::ReturnReplayProtection));
|
||||
let checker = TransactionReturnReplayProtection::new(CanonTransaction::new(&transaction), &consensus, 50);
|
||||
let checker = TransactionReturnReplayProtection::new(CanonTransaction::new(&transaction), &consensus, consensus.fork.activation_height() - 1);
|
||||
assert_eq!(checker.check(), Ok(()));
|
||||
|
||||
let consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::NoFork);
|
||||
|
|
|
@ -34,7 +34,7 @@ impl<'a> HeaderProofOfWork<'a> {
|
|||
fn new(header: &'a IndexedBlockHeader, network: Network) -> Self {
|
||||
HeaderProofOfWork {
|
||||
header: header,
|
||||
max_work_bits: network.max_bits(),
|
||||
max_work_bits: network.max_bits().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use std::cmp;
|
||||
use primitives::compact::Compact;
|
||||
use primitives::hash::H256;
|
||||
use primitives::bigint::U256;
|
||||
use primitives::bigint::{Uint, U256};
|
||||
use chain::BlockHeader;
|
||||
use network::{Network, ConsensusParams, ConsensusFork};
|
||||
use db::{BlockHeaderProvider, BlockRef};
|
||||
use timestamp::median_timestamp_inclusive;
|
||||
|
||||
use constants::{
|
||||
DOUBLE_SPACING_SECONDS,
|
||||
TARGET_SPACING_SECONDS, DOUBLE_SPACING_SECONDS,
|
||||
TARGET_TIMESPAN_SECONDS, MIN_TIMESPAN, MAX_TIMESPAN, RETARGETING_INTERVAL
|
||||
};
|
||||
|
||||
|
@ -57,13 +58,26 @@ pub fn retarget_timespan(retarget_timestamp: u32, last_timestamp: u32) -> u32 {
|
|||
|
||||
/// Returns work required for given header
|
||||
pub fn work_required(parent_hash: H256, time: u32, height: u32, store: &BlockHeaderProvider, consensus: &ConsensusParams) -> Compact {
|
||||
let max_bits = consensus.network.max_bits();
|
||||
let max_bits = consensus.network.max_bits().into();
|
||||
if height == 0 {
|
||||
return max_bits;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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");
|
||||
|
@ -84,7 +98,7 @@ 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(fork_height) if height >= fork_height => {
|
||||
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
|
||||
|
@ -93,8 +107,9 @@ pub fn work_required(parent_hash: H256, time: u32, height: u32, store: &BlockHea
|
|||
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 parent_timestamp = median_timestamp_inclusive(parent_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
|
||||
|
@ -122,8 +137,9 @@ pub fn work_required_testnet(parent_hash: H256, time: u32, height: u32, store: &
|
|||
|
||||
let parent_header = store.block_header(block_ref.clone()).expect("height != 0; qed");
|
||||
let max_time_gap = parent_header.time + DOUBLE_SPACING_SECONDS;
|
||||
let max_bits = network.max_bits().into();
|
||||
if time > max_time_gap {
|
||||
return network.max_bits();
|
||||
return max_bits;
|
||||
}
|
||||
|
||||
// TODO: optimize it, so it does not make 2016!!! redundant queries each time
|
||||
|
@ -137,12 +153,12 @@ pub fn work_required_testnet(parent_hash: H256, time: u32, height: u32, store: &
|
|||
}
|
||||
|
||||
for (index, bit) in bits.into_iter().enumerate() {
|
||||
if bit != network.max_bits() || is_retarget_height(height - index as u32 - 1) {
|
||||
if bit != max_bits || is_retarget_height(height - index as u32 - 1) {
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
network.max_bits()
|
||||
max_bits
|
||||
}
|
||||
|
||||
/// Algorithm used for retargeting work every 2 weeks
|
||||
|
@ -160,6 +176,120 @@ 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 }
|
||||
|
@ -171,11 +301,39 @@ 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, ConsensusFork};
|
||||
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};
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) &&
|
||||
|
@ -185,14 +343,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_is_valid_proof_of_work() {
|
||||
// block 2
|
||||
assert!(is_valid_pow(Network::Mainnet.max_bits(), 486604799u32, "000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"));
|
||||
assert!(is_valid_pow(Network::Mainnet.max_bits().into(), 486604799u32, "000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd"));
|
||||
// block 400_000
|
||||
assert!(is_valid_pow(Network::Mainnet.max_bits(), 403093919u32, "000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f"));
|
||||
assert!(is_valid_pow(Network::Mainnet.max_bits().into(), 403093919u32, "000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f"));
|
||||
|
||||
// other random tests
|
||||
assert!(is_valid_pow(Network::Regtest.max_bits(), 0x181bc330u32, "00000000000000001bc330000000000000000000000000000000000000000000"));
|
||||
assert!(!is_valid_pow(Network::Regtest.max_bits(), 0x181bc330u32, "00000000000000001bc330000000000000000000000000000000000000000001"));
|
||||
assert!(!is_valid_pow(Network::Regtest.max_bits(), 0x181bc330u32, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
|
||||
assert!(is_valid_pow(Network::Regtest.max_bits().into(), 0x181bc330u32, "00000000000000001bc330000000000000000000000000000000000000000000"));
|
||||
assert!(!is_valid_pow(Network::Regtest.max_bits().into(), 0x181bc330u32, "00000000000000001bc330000000000000000000000000000000000000000001"));
|
||||
assert!(!is_valid_pow(Network::Regtest.max_bits().into(), 0x181bc330u32, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -211,34 +369,11 @@ mod tests {
|
|||
// https://github.com/bitcoinclassic/bitcoinclassic/blob/8bf1fb856df44d1b790b0b835e4c1969be736e25/src/test/pow_tests.cpp#L108
|
||||
#[test]
|
||||
fn bitcoin_cash_req7() {
|
||||
#[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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let main_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::NoFork);
|
||||
let uahf_consensus = ConsensusParams::new(Network::Mainnet, ConsensusFork::BitcoinCash(1000));
|
||||
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,
|
||||
|
@ -282,4 +417,228 @@ mod tests {
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue