bitcoin-dogecoin 0.32.7-doge.0

General purpose library for using and interoperating with Bitcoin and Dogecoin.
Documentation
// SPDX-License-Identifier: Apache-2.0

//! Dogecoin consensus parameters.
//!
//! This module provides a predefined set of parameters for different Dogecoin
//! chains (such as mainnet, testnet, regtest).
//!

use crate::dogecoin::Network;
use crate::Target;


const ONE_SECOND: i64 = 1;
const ONE_MINUTE: i64 = 60;
const FOUR_HOURS: i64 = 4 * 60 * 60;

/// Parameters that influence chain consensus.
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct Params {
    /// Network for which parameters are valid.
    pub network: Network,
    /// Time when BIP16 becomes active.
    pub bip16_time: u32,
    /// Block height at which BIP34 becomes active.
    pub bip34_height: u32,
    /// Block height at which BIP65 becomes active.
    pub bip65_height: u32,
    /// Block height at which BIP66 becomes active.
    pub bip66_height: u32,
    /// Minimum blocks including miner confirmation.
    pub rule_change_activation_threshold: u32,
    /// Number of blocks with the same set of rules.
    pub miner_confirmation_window: u32,
    /// Proof of work limit value. It contains the lowest possible difficulty.
    #[deprecated(since = "0.32.0", note = "field renamed to max_attainable_target")]
    pub pow_limit: Target,
    /// The maximum **attainable** target value for these params.
    ///
    /// Not all target values are attainable because consensus code uses the compact format to
    /// represent targets (see [`crate::CompactTarget`]).
    ///
    /// Note that this value differs from Dogecoin Core's powLimit field in that this value is
    /// attainable, but Dogecoin Core's is not. Specifically, because targets in Bitcoin are always
    /// rounded to the nearest float expressible in "compact form", not all targets are attainable.
    /// Still, this should not affect consensus as the only place where the non-compact form of
    /// this is used in Dogecoin Core's consensus algorithm is in comparison and there are no
    /// compact-expressible values between Dogecoin Core's and the limit expressed here.
    pub max_attainable_target: Target,
    /// Expected amount of time to mine one block.
    pub pow_target_spacing: i64,
    /// Whether retargeting is disabled for this network or not.
    pub no_pow_retargeting: bool,
    /// Height after which the Digishield difficulty adjustment algorithm is used.
    pub digishield_activation_height: u32,
    /// Height after which merged mining is allowed.
    pub auxpow_height: u32,
    /// Whether to enforce that the parent and auxiliary block headers have different chain IDs.
    pub strict_chain_id: bool,
    /// Expected chain ID for validating AuxPoW headers.
    pub auxpow_chain_id: i32,
}

/// The mainnet parameters.
///
/// Use this for a static reference e.g., `&params::MAINNET`.
///
/// For more on static vs const see The Rust Reference [using-statics-or-consts] section.
///
/// [using-statics-or-consts]: <https://doc.rust-lang.org/reference/items/static-items.html#using-statics-or-consts>
pub static MAINNET: Params = Params::MAINNET;
/// The dogecoin testnet parameters.
pub static TESTNET: Params = Params::TESTNET;
/// The dogecoin regtest parameters.
pub static REGTEST: Params = Params::REGTEST;

impl Params {
    /// The mainnet parameters (alias for `Params::MAINNET`).
    pub const DOGECOIN: Params = Params::MAINNET;

    /// The mainnet parameters.
    /// Ref: <https://github.com/dogecoin/dogecoin/blob/2c513d0172e8bc86fe9a337693b26f2fdf68a013/src/chainparams.cpp#L75>
    pub const MAINNET: Params = Params {
            network: Network::Dogecoin,
            bip16_time: 1333238400,                 // Apr 1 2012
            bip34_height: 1034383, // 80d1364201e5df97e696c03bdd24dc885e8617b9de51e453c10a4f629b1e797a
            bip65_height: 3464751, // 34cd2cbba4ba366f47e5aa0db5f02c19eba2adf679ceb6653ac003bdc9a0ef1f
            bip66_height: 1034383, // 80d1364201e5df97e696c03bdd24dc885e8617b9de51e453c10a4f629b1e797a
            rule_change_activation_threshold: 9576, // 95% of 10,080
            miner_confirmation_window: 10080, // 60 * 24 * 7 = 10,080 blocks, or one week
            pow_limit: Target::MAX_ATTAINABLE_MAINNET_DOGE,
            max_attainable_target: Target::MAX_ATTAINABLE_MAINNET_DOGE,
            pow_target_spacing: ONE_MINUTE,           // 1 minute
            no_pow_retargeting: false,
            auxpow_height: 371_337,
            strict_chain_id: true,
            auxpow_chain_id: 0x0062,
            digishield_activation_height: 145000,
    };

    /// The Dogecoin testnet parameters.
    /// Ref: <https://github.com/dogecoin/dogecoin/blob/2c513d0172e8bc86fe9a337693b26f2fdf68a013/src/chainparams.cpp#L229>
    pub const TESTNET: Params = Params {
            network: Network::Testnet,
            bip16_time: 1333238400,                 // Apr 1 2012
            bip34_height: 708658, // 21b8b97dcdb94caa67c7f8f6dbf22e61e0cfe0e46e1fff3528b22864659e9b38
            bip65_height: 1854705, // 955bd496d23790aba1ecfacb722b089a6ae7ddabaedf7d8fb0878f48308a71f9
            bip66_height: 708658, // 21b8b97dcdb94caa67c7f8f6dbf22e61e0cfe0e46e1fff3528b22864659e9b38
            rule_change_activation_threshold: 2880, // 2 days (note this is significantly lower than Bitcoin standard)
            miner_confirmation_window: 10080,       // 60 * 24 * 7 = 10,080 blocks, or one week
            pow_limit: Target::MAX_ATTAINABLE_TESTNET_DOGE,
            max_attainable_target: Target::MAX_ATTAINABLE_TESTNET_DOGE,
            pow_target_spacing: ONE_MINUTE,           // 1 minute
            no_pow_retargeting: false,
            auxpow_height: 158_100,
            strict_chain_id: false,
            auxpow_chain_id: 0x0062,
            digishield_activation_height: 145000,
    };

    /// The Dogecoin regtest parameters.
    /// Ref: <https://github.com/dogecoin/dogecoin/blob/2c513d0172e8bc86fe9a337693b26f2fdf68a013/src/chainparams.cpp#L382>
    pub const REGTEST: Params = Params {
            network: Network::Regtest,
            bip16_time: 1333238400,  // Apr 1 2012
            bip34_height: 100000000, // not activated on regtest
            bip65_height: 1351,
            bip66_height: 1251,                    // used only in rpc tests
            rule_change_activation_threshold: 540, // 75%
            miner_confirmation_window: 720,
            pow_limit: Target::MAX_ATTAINABLE_REGTEST_DOGE,
            max_attainable_target: Target::MAX_ATTAINABLE_REGTEST_DOGE,
            pow_target_spacing: ONE_SECOND,            // regtest: 1 second blocks
            no_pow_retargeting: true,
            auxpow_height: 20,
            strict_chain_id: true,
            auxpow_chain_id: 0x0062,
            digishield_activation_height: 10,
    };

    /// Creates parameters set for the given network.
    pub const fn new(network: Network) -> Self {
        match network {
            Network::Dogecoin => Params::MAINNET,
            Network::Testnet => Params::TESTNET,
            Network::Regtest => Params::REGTEST,
        }
    }

    /// Checks if Digishield difficulty adjustment is activated at the given block height.
    pub const fn is_digishield_activated(&self, height: u32) -> bool {
         height >= self.digishield_activation_height
    }

    /// Returns the target timespan (in seconds) used for PoW retargeting at the given block height.
    pub const fn pow_target_timespan(&self, height: u32) -> i64 {
        if !self.is_digishield_activated(height) {
            FOUR_HOURS
        } else {
            match self.network {
                Network::Dogecoin => ONE_MINUTE,
                Network::Testnet => ONE_MINUTE,
                Network::Regtest => ONE_SECOND,
            }
        }
    }

    /// Determines whether minimal difficulty may be used for mining blocks.
    pub const fn allow_min_difficulty_blocks(&self, height: u32) -> bool {
        match self.network {
            Network::Dogecoin => false,
            Network::Testnet => match height {
                0..=144_999 => true,
                145_000..=157_499 => false,
                157_500.. => true,
            }
            Network::Regtest => true,
        }
    }

    /// Checks if legacy blocks can be mined at the given block height.
    pub const fn allow_legacy_blocks(&self, height: u32) -> bool {
        height < self.auxpow_height
    }
}

impl AsRef<Params> for Params {
    fn as_ref(&self) -> &Params { self }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn digishield_activation() {
        let pre_digishield_heights = [5_000, 10_000, 144_999];
        let digishield_heights = [145_000, 145_001, 1_000_000];
        let params = [Params::MAINNET, Params::TESTNET];
        for param in params {
            for &height in pre_digishield_heights.iter() {
                assert!(!param.is_digishield_activated(height));
            }
            for &height in digishield_heights.iter() {
                assert!(param.is_digishield_activated(height));
            }
        }

        let pre_digishield_heights_regtest = [0, 5, 9];
        let digishield_heights_regtest = [10, 11, 100];
        for &height in pre_digishield_heights_regtest.iter() {
            assert!(!Params::REGTEST.is_digishield_activated(height));
        }
        for &height in digishield_heights_regtest.iter() {
            assert!(Params::REGTEST.is_digishield_activated(height));
        }
    }

    #[test]
    fn hardfork_parameters() {
        let params = &Params::MAINNET;

        // Initial parameters (pre-Digishield era)
        let initial_height = 0;
        assert_eq!(params.pow_target_timespan(initial_height), 14400); // 4 hours
        assert!(params.allow_legacy_blocks(initial_height));
        assert!(!params.is_digishield_activated(initial_height));

        let initial_end_height = 144999;
        assert_eq!(params.pow_target_timespan(initial_end_height), 14400); // 4 hours
        assert!(params.allow_legacy_blocks(initial_end_height));
        assert!(!params.is_digishield_activated(initial_end_height));

        // Digishield parameters
        let digishield_height = 145000;
        assert_eq!(params.pow_target_timespan(digishield_height), 60); // 1 minute
        assert!(params.allow_legacy_blocks(digishield_height));
        assert!(params.is_digishield_activated(digishield_height));

        let digishield_end_height = 371336;
        assert_eq!(params.pow_target_timespan(digishield_end_height), 60); // 1 minute
        assert!(params.allow_legacy_blocks(digishield_end_height));
        assert!(params.is_digishield_activated(digishield_end_height));

        // AuxPow parameters
        let auxpow_height = 371337;
        assert_eq!(params.pow_target_timespan(auxpow_height), 60); // 1 minute
        assert!(!params.allow_legacy_blocks(auxpow_height));
        assert!(params.is_digishield_activated(auxpow_height));

        // Arbitrary point after last hard-fork
        let auxpow_high_height = 700000;
        assert_eq!(params.pow_target_timespan(auxpow_high_height), 60); // 1 minute
        assert!(!params.allow_legacy_blocks(auxpow_high_height));
        assert!(params.is_digishield_activated(auxpow_high_height));
    }
}