pub use super::{ bps::{Bps, TenBps}, constants::consensus::*, genesis::{DEVNET_GENESIS, GENESIS, GenesisBlock, SIMNET_GENESIS, TESTNET_GENESIS}, }; use crate::{ BlockLevel, KType, constants::{BLOCK_VERSION, STORAGE_MASS_PARAMETER}, mass::{BlockLaneLimits, BlockMassLimits, MassCofactors}, network::{NetworkId, NetworkType}, }; use kaspa_addresses::Prefix; use kaspa_math::Uint256; use serde::{Deserialize, Serialize}; use std::{ cmp::min, ops::{Deref, DerefMut}, }; // Increased for stark proofs. This value is effectively covered by the // transient block mass limit: 1_000_000 transient mass / 4 grams-per-byte = 250_000 // bytes for the entire block, so a larger signature script cannot be accepted anyway. // TODO: check whether this early signature-script length guard can be // removed entirely, or whether it remains useful as cheap early protection. const MAX_SIGNATURE_SCRIPT_LEN: usize = 250_000; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ForkActivation(u64); impl ForkActivation { const NEVER: u64 = u64::MAX; const ALWAYS: u64 = 0; pub const fn new(daa_score: u64) -> Self { Self(daa_score) } pub const fn never() -> Self { Self(Self::NEVER) } pub const fn always() -> Self { Self(Self::ALWAYS) } /// Returns the actual DAA score triggering the activation. Should be used only /// for cases where the explicit value is required for computations (e.g., coinbase subsidy). /// Otherwise, **activation checks should always go through `self.is_active(..)`** pub fn daa_score(self) -> u64 { self.0 } pub fn is_active(self, current_daa_score: u64) -> bool { current_daa_score >= self.0 } pub fn delayed_by(self, daa_score_delta: u64) -> Self { match self.0 { Self::ALWAYS | Self::NEVER => self, daa_score => Self(daa_score.saturating_add(daa_score_delta)), } } pub fn early_by(self, daa_score_delta: u64) -> Self { match self.0 { Self::ALWAYS | Self::NEVER => self, daa_score => Self(daa_score.saturating_sub(daa_score_delta)), } } /// Checks if the fork was "recently" activated, i.e., in the time frame of the provided range. /// This function returns false for forks that were always active, since they were never activated. pub fn is_within_range_from_activation(self, current_daa_score: u64, range: u64) -> bool { self != Self::always() && self.is_active(current_daa_score) && current_daa_score < self.0 + range } /// Checks if the fork is expected to be activated "soon", i.e., in the time frame of the provided range. /// Returns the distance from activation if so, or `None` otherwise. pub fn is_within_range_before_activation(self, current_daa_score: u64, range: u64) -> Option { if !self.is_active(current_daa_score) && current_daa_score + range > self.0 { Some(self.0 - current_daa_score) } else { None } } } /// A consensus parameter which depends on forking activation #[derive(Clone, Copy, Debug)] pub struct ForkedParam { pre: T, post: T, activation: ForkActivation, } impl ForkedParam { const fn new(pre: T, post: T, activation: ForkActivation) -> Self { Self { pre, post, activation } } pub const fn new_const(val: T) -> Self { Self { pre: val, post: val, activation: ForkActivation::never() } } pub fn activation(&self) -> ForkActivation { self.activation } pub fn get(&self, daa_score: u64) -> T { if self.activation.is_active(daa_score) { self.post } else { self.pre } } pub fn with_delayed_activation(&self, delay_daa_score: u64) -> Self { Self::new(self.pre, self.post, self.activation.delayed_by(delay_daa_score)) } /// Returns the value before activation (=pre unless activation = always) pub fn before(&self) -> T { match self.activation.0 { ForkActivation::ALWAYS => self.post, _ => self.pre, } } /// Returns the permanent long-term value after activation (=post unless the activation is never scheduled) pub fn after(&self) -> T { match self.activation.0 { ForkActivation::NEVER => self.pre, _ => self.post, } } /// Returns the configured post-fork value regardless of whether activation is scheduled. pub fn raw_post(&self) -> T { self.post } /// Maps the ForkedParam to a new ForkedParam by applying a map function on both pre and post pub fn map U>(&self, f: F) -> ForkedParam { ForkedParam::new(f(self.pre), f(self.post), self.activation) } } impl From for ForkedParam { fn from(value: T) -> Self { Self::new_const(value) } } impl ForkedParam { /// Returns the min of `pre` and `post` values. Useful for non-consensus initializations /// which require knowledge of the value bounds. /// /// Note that if activation is not scheduled (set to never) then pre is always returned, /// and if activation is set to always (since inception), post will be returned. pub fn lower_bound(&self) -> T { match self.activation.0 { ForkActivation::NEVER => self.pre, ForkActivation::ALWAYS => self.post, _ => self.pre.min(self.post), } } /// Returns the max of `pre` and `post` values. Useful for non-consensus initializations /// which require knowledge of the value bounds. /// /// Note that if activation is not scheduled (set to never) then pre is always returned, /// and if activation is set to always (since inception), post will be returned. pub fn upper_bound(&self) -> T { match self.activation.0 { ForkActivation::NEVER => self.pre, ForkActivation::ALWAYS => self.post, _ => self.pre.max(self.post), } } } /// Blockrate-related consensus params. /// Grouped together under a single struct because they are logically related and /// in order to easily support **future BPS acceleration hardforks** (by simply adding /// a forked instance of blockrate params to the main [`Params`]). #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct BlockrateParams { pub target_time_per_block: u64, // (milliseconds) pub ghostdag_k: KType, pub past_median_time_sample_rate: u64, pub difficulty_sample_rate: u64, pub max_block_parents: u8, pub mergeset_size_limit: u64, pub merge_depth: u64, pub finality_depth: u64, pub pruning_depth: u64, pub coinbase_maturity: u64, } impl BlockrateParams { pub const fn new() -> Self { Self { target_time_per_block: Bps::::target_time_per_block(), ghostdag_k: Bps::::ghostdag_k(), past_median_time_sample_rate: Bps::::past_median_time_sample_rate(), difficulty_sample_rate: Bps::::difficulty_adjustment_sample_rate(), max_block_parents: Bps::::max_block_parents(), mergeset_size_limit: Bps::::mergeset_size_limit(), merge_depth: Bps::::merge_depth_bound(), finality_depth: Bps::::finality_depth(), pruning_depth: Bps::::pruning_depth(), coinbase_maturity: Bps::::coinbase_maturity(), } } pub const fn increase_max_block_parents(mut self, max_block_parents: u8) -> Self { if self.max_block_parents < max_block_parents { self.max_block_parents = max_block_parents; } self } } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct OverrideParams { /// Timestamp deviation tolerance (in seconds) pub timestamp_deviation_tolerance: Option, /// Size of the sampled block window that is used to calculate the past median time of each block pub past_median_time_window_size: Option, /// Size of the sampled block window that is used to calculate the required difficulty of each block pub difficulty_window_size: Option, /// The minimum size a difficulty window (full or sampled) must have to trigger a DAA calculation pub min_difficulty_window_size: Option, pub coinbase_payload_script_public_key_max_len: Option, pub max_coinbase_payload_len: Option, pub max_tx_inputs: Option, pub max_tx_outputs: Option, pub max_signature_script_len: Option, pub max_script_public_key_len: Option, pub mass_per_tx_byte: Option, pub mass_per_script_pub_key_byte: Option, pub mass_per_sig_op: Option, pub block_mass_limits: Option, pub block_lane_limits: Option, /// The parameter for scaling inverse KAS value to mass units (KIP-0009) pub storage_mass_parameter: Option, /// DAA score after which the pre-deflationary period switches to the deflationary period pub deflationary_phase_daa_score: Option, pub pre_deflationary_phase_base_subsidy: Option, pub skip_proof_of_work: Option, pub max_block_level: Option, pub pruning_proof_m: Option, /// Blockrate-related params pub blockrate: Option, /// Target time per block prior to the crescendo hardfork (in milliseconds) pub pre_crescendo_target_time_per_block: Option, /// Crescendo activation DAA score pub crescendo_activation: Option, } impl From for OverrideParams { fn from(p: Params) -> Self { Self { timestamp_deviation_tolerance: Some(p.timestamp_deviation_tolerance), pre_crescendo_target_time_per_block: Some(p.pre_crescendo_target_time_per_block), difficulty_window_size: Some(p.difficulty_window_size), past_median_time_window_size: Some(p.past_median_time_window_size), min_difficulty_window_size: Some(p.min_difficulty_window_size), coinbase_payload_script_public_key_max_len: Some(p.coinbase_payload_script_public_key_max_len), max_coinbase_payload_len: Some(p.max_coinbase_payload_len), max_tx_inputs: Some(p.max_tx_inputs), max_tx_outputs: Some(p.max_tx_outputs), max_signature_script_len: Some(p.max_signature_script_len), max_script_public_key_len: Some(p.max_script_public_key_len), mass_per_tx_byte: Some(p.mass_per_tx_byte), mass_per_script_pub_key_byte: Some(p.mass_per_script_pub_key_byte), mass_per_sig_op: Some(p.mass_per_sig_op), block_mass_limits: Some(p.block_mass_limits), block_lane_limits: Some(p.block_lane_limits), storage_mass_parameter: Some(p.storage_mass_parameter), deflationary_phase_daa_score: Some(p.deflationary_phase_daa_score), pre_deflationary_phase_base_subsidy: Some(p.pre_deflationary_phase_base_subsidy), skip_proof_of_work: Some(p.skip_proof_of_work), max_block_level: Some(p.max_block_level), pruning_proof_m: Some(p.pruning_proof_m), blockrate: Some(p.blockrate), crescendo_activation: Some(p.crescendo_activation), } } } /// Consensus parameters. Contains settings and configurations which are consensus-sensitive. /// Changing one of these on a network node would exclude and prevent it from reaching consensus /// with the other unmodified nodes. #[derive(Clone, Debug)] pub struct Params { pub dns_seeders: &'static [&'static str], pub net: NetworkId, pub genesis: GenesisBlock, /// Timestamp deviation tolerance (in seconds) pub timestamp_deviation_tolerance: u64, /// Defines the highest allowed proof of work difficulty value for a block as a [`Uint256`] pub max_difficulty_target: Uint256, /// Highest allowed proof of work difficulty as a floating number pub max_difficulty_target_f64: f64, /// Size of the sampled block window that is used to calculate the past median time of each block pub past_median_time_window_size: usize, /// Size of the sampled block window that is used to calculate the required difficulty of each block pub difficulty_window_size: usize, /// The minimum size a difficulty window must have to trigger a DAA calculation pub min_difficulty_window_size: usize, pub coinbase_payload_script_public_key_max_len: u8, pub max_coinbase_payload_len: usize, pub max_tx_inputs: usize, pub max_tx_outputs: usize, pub max_signature_script_len: usize, pub max_script_public_key_len: usize, pub mass_per_tx_byte: u64, pub mass_per_script_pub_key_byte: u64, pub mass_per_sig_op: u64, pub block_mass_limits: BlockMassLimits, pub block_lane_limits: BlockLaneLimits, /// The parameter for scaling inverse KAS value to mass units (KIP-0009) pub storage_mass_parameter: u64, /// DAA score after which the pre-deflationary period switches to the deflationary period pub deflationary_phase_daa_score: u64, pub pre_deflationary_phase_base_subsidy: u64, pub skip_proof_of_work: bool, pub max_block_level: BlockLevel, pub pruning_proof_m: u64, /// Blockrate-related params pub blockrate: BlockrateParams, /// Target time per block prior to the crescendo hardfork (in milliseconds). /// Required permanently in order to calculate the subsidy month from the current DAA score pub pre_crescendo_target_time_per_block: u64, /// Crescendo activation DAA score pub crescendo_activation: ForkActivation, } impl Params { /// Returns the past median time sample rate #[inline] #[must_use] pub fn past_median_time_sample_rate(&self) -> u64 { self.blockrate.past_median_time_sample_rate } /// Returns the difficulty sample rate #[inline] #[must_use] pub fn difficulty_sample_rate(&self) -> u64 { self.blockrate.difficulty_sample_rate } /// Returns the target time per block (milliseconds) #[inline] #[must_use] pub fn target_time_per_block(&self) -> u64 { self.blockrate.target_time_per_block } /// Returns the expected number of blocks per second #[inline] #[must_use] pub fn bps(&self) -> u64 { 1000 / self.blockrate.target_time_per_block } /// Returns the expected number of blocks per second throughout history (currently represented as [`ForkedParam`]). /// Required permanently in order to calculate the subsidy month from the current DAA score. #[inline] #[must_use] pub fn bps_history(&self) -> ForkedParam { ForkedParam::new( 1000 / self.pre_crescendo_target_time_per_block, 1000 / self.blockrate.target_time_per_block, self.crescendo_activation, ) } /// Returns the cofactors for normalizing block mass dimensions. #[inline] #[must_use] pub fn block_mass_cofactors(&self) -> MassCofactors { self.block_mass_limits.cofactors() } pub fn ghostdag_k(&self) -> KType { self.blockrate.ghostdag_k } pub fn max_block_parents(&self) -> u8 { self.blockrate.max_block_parents } pub fn mergeset_size_limit(&self) -> u64 { self.blockrate.mergeset_size_limit } pub fn merge_depth(&self) -> u64 { self.blockrate.merge_depth } pub fn finality_depth(&self) -> u64 { self.blockrate.finality_depth } pub fn pruning_depth(&self) -> u64 { self.blockrate.pruning_depth } pub fn coinbase_maturity(&self) -> u64 { self.blockrate.coinbase_maturity } pub fn finality_duration_in_milliseconds(&self) -> u64 { self.blockrate.target_time_per_block * self.blockrate.finality_depth } pub fn difficulty_window_duration_in_block_units(&self) -> u64 { self.blockrate.difficulty_sample_rate * self.difficulty_window_size as u64 } pub fn expected_difficulty_window_duration_in_milliseconds(&self) -> u64 { self.blockrate.target_time_per_block * self.blockrate.difficulty_sample_rate * self.difficulty_window_size as u64 } /// Returns the depth at which the anticone of a chain block is final (i.e., is a permanently closed set). /// Based on the analysis at /// and on the decomposition of merge depth (rule R-I therein) from finality depth (φ) pub fn anticone_finalization_depth(&self) -> u64 { let anticone_finalization_depth = self.blockrate.finality_depth + self.blockrate.merge_depth + 4 * self.blockrate.mergeset_size_limit * self.blockrate.ghostdag_k as u64 + 2 * self.blockrate.ghostdag_k as u64 + 2; // In mainnet it's guaranteed that `self.pruning_depth` is greater // than `anticone_finalization_depth`, but for some tests we use // a smaller (unsafe) pruning depth, so we return the minimum of // the two to avoid a situation where a block can be pruned and // not finalized. min(self.blockrate.pruning_depth, anticone_finalization_depth) } pub fn block_version(&self) -> u16 { BLOCK_VERSION } pub fn network_name(&self) -> String { self.net.to_prefixed() } pub fn prefix(&self) -> Prefix { self.net.into() } pub fn default_p2p_port(&self) -> u16 { self.net.default_p2p_port() } pub fn default_rpc_port(&self) -> u16 { self.net.default_rpc_port() } pub fn override_params(self, overrides: OverrideParams) -> Self { Self { dns_seeders: self.dns_seeders, net: self.net, genesis: self.genesis.clone(), timestamp_deviation_tolerance: overrides.timestamp_deviation_tolerance.unwrap_or(self.timestamp_deviation_tolerance), max_difficulty_target: self.max_difficulty_target, max_difficulty_target_f64: self.max_difficulty_target_f64, difficulty_window_size: overrides.difficulty_window_size.unwrap_or(self.difficulty_window_size), past_median_time_window_size: overrides.past_median_time_window_size.unwrap_or(self.past_median_time_window_size), min_difficulty_window_size: overrides.min_difficulty_window_size.unwrap_or(self.min_difficulty_window_size), coinbase_payload_script_public_key_max_len: overrides .coinbase_payload_script_public_key_max_len .unwrap_or(self.coinbase_payload_script_public_key_max_len), max_coinbase_payload_len: overrides.max_coinbase_payload_len.unwrap_or(self.max_coinbase_payload_len), max_tx_inputs: overrides.max_tx_inputs.unwrap_or(self.max_tx_inputs), max_tx_outputs: overrides.max_tx_outputs.unwrap_or(self.max_tx_outputs), max_signature_script_len: overrides.max_signature_script_len.unwrap_or(self.max_signature_script_len), max_script_public_key_len: overrides.max_script_public_key_len.unwrap_or(self.max_script_public_key_len), mass_per_tx_byte: overrides.mass_per_tx_byte.unwrap_or(self.mass_per_tx_byte), mass_per_script_pub_key_byte: overrides.mass_per_script_pub_key_byte.unwrap_or(self.mass_per_script_pub_key_byte), mass_per_sig_op: overrides.mass_per_sig_op.unwrap_or(self.mass_per_sig_op), block_mass_limits: overrides.block_mass_limits.unwrap_or(self.block_mass_limits), block_lane_limits: overrides.block_lane_limits.unwrap_or(self.block_lane_limits), storage_mass_parameter: overrides.storage_mass_parameter.unwrap_or(self.storage_mass_parameter), deflationary_phase_daa_score: overrides.deflationary_phase_daa_score.unwrap_or(self.deflationary_phase_daa_score), pre_deflationary_phase_base_subsidy: overrides .pre_deflationary_phase_base_subsidy .unwrap_or(self.pre_deflationary_phase_base_subsidy), skip_proof_of_work: overrides.skip_proof_of_work.unwrap_or(self.skip_proof_of_work), max_block_level: overrides.max_block_level.unwrap_or(self.max_block_level), pruning_proof_m: overrides.pruning_proof_m.unwrap_or(self.pruning_proof_m), blockrate: overrides.blockrate.clone().unwrap_or(self.blockrate.clone()), pre_crescendo_target_time_per_block: overrides .pre_crescendo_target_time_per_block .unwrap_or(self.pre_crescendo_target_time_per_block), crescendo_activation: overrides.crescendo_activation.unwrap_or(self.crescendo_activation), } } } impl Deref for Params { type Target = BlockrateParams; fn deref(&self) -> &Self::Target { &self.blockrate } } impl DerefMut for Params { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.blockrate } } impl From for Params { fn from(value: NetworkType) -> Self { match value { NetworkType::Mainnet => MAINNET_PARAMS, NetworkType::Testnet => TESTNET_PARAMS, NetworkType::Devnet => DEVNET_PARAMS, NetworkType::Simnet => SIMNET_PARAMS, } } } impl From for Params { fn from(value: NetworkId) -> Self { match value.network_type { NetworkType::Mainnet => MAINNET_PARAMS, NetworkType::Testnet => match value.suffix { Some(10) => TESTNET_PARAMS, Some(x) => panic!("Testnet suffix {} is not supported", x), None => panic!("Testnet suffix not provided"), }, NetworkType::Devnet => DEVNET_PARAMS, NetworkType::Simnet => SIMNET_PARAMS, } } } pub const MAINNET_PARAMS: Params = Params { dns_seeders: &[ // This DNS seeder is run by Denis Mashkevich "mainnet-dnsseed-1.kaspanet.org", // This DNS seeder is run by Denis Mashkevich "mainnet-dnsseed-2.kaspanet.org", // This DNS seeder is run by Georges Künzli "seeder1.kaspad.net", // This DNS seeder is run by Georges Künzli "seeder2.kaspad.net", // This DNS seeder is run by Georges Künzli "seeder3.kaspad.net", // This DNS seeder is run by Georges Künzli "seeder4.kaspad.net", // This DNS seeder is run by Tim "kaspadns.kaspacalc.net", // This DNS seeder is run by supertypo "n-mainnet.kaspa.ws", // This DNS seeder is run by -gerri- "dnsseeder-kaspa-mainnet.x-con.at", ], net: NetworkId::new(NetworkType::Mainnet), genesis: GENESIS, timestamp_deviation_tolerance: TIMESTAMP_DEVIATION_TOLERANCE, max_difficulty_target: MAX_DIFFICULTY_TARGET, max_difficulty_target_f64: MAX_DIFFICULTY_TARGET_AS_F64, past_median_time_window_size: MEDIAN_TIME_SAMPLED_WINDOW_SIZE as usize, difficulty_window_size: DIFFICULTY_SAMPLED_WINDOW_SIZE as usize, min_difficulty_window_size: MIN_DIFFICULTY_WINDOW_SIZE, coinbase_payload_script_public_key_max_len: 150, max_coinbase_payload_len: 204, // Limit the cost of calculating compute/transient/storage masses max_tx_inputs: 1000, max_tx_outputs: 1000, // Transient mass caps the entire block's transient footprint at 250KB, so no individual signature script can exceed it. max_signature_script_len: MAX_SIGNATURE_SCRIPT_LEN, // Retain a 10KB per-output guard; compute mass caps aggregate script-public-key bytes at roughly 45.5KB per block. // Note that storage mass will kick in and gradually penalize also for lower lengths (generalized KIP-0009, plurality will be high). max_script_public_key_len: 10_000, mass_per_tx_byte: 1, mass_per_script_pub_key_byte: 10, mass_per_sig_op: 1000, block_mass_limits: BlockMassLimits { compute: 500_000, storage: 500_000, transient: 1_000_000 }, block_lane_limits: BlockLaneLimits { lanes_per_block: DEFAULT_LANES_PER_BLOCK_LIMIT, gas_per_lane: DEFAULT_GAS_PER_LANE_LIMIT }, storage_mass_parameter: STORAGE_MASS_PARAMETER, // deflationary_phase_daa_score is the DAA score after which the pre-deflationary period // switches to the deflationary period. This number is calculated as follows: // We define a year as 365.25 days // Half a year in seconds = 365.25 / 2 * 24 * 60 * 60 = 15778800 // The network was down for three days shortly after launch // Three days in seconds = 3 * 24 * 60 * 60 = 259200 deflationary_phase_daa_score: 15778800 - 259200, pre_deflationary_phase_base_subsidy: 50000000000, skip_proof_of_work: false, max_block_level: 225, pruning_proof_m: 1000, blockrate: BlockrateParams::new::<10>(), pre_crescendo_target_time_per_block: 1000, // Roughly 2025-05-05 1500 UTC crescendo_activation: ForkActivation::new(110_165_000), }; pub const TESTNET_PARAMS: Params = Params { dns_seeders: &[ // This DNS seeder is run by Tiram "seeder1-tn.kaspad.net", // This DNS seeder is run by -gerri- "dnsseeder-kaspa-testnet.x-con.at", // This DNS seeder is run by supertypo "n-testnet-10.kaspa.ws", ], net: NetworkId::with_suffix(NetworkType::Testnet, 10), genesis: TESTNET_GENESIS, timestamp_deviation_tolerance: TIMESTAMP_DEVIATION_TOLERANCE, max_difficulty_target: MAX_DIFFICULTY_TARGET, max_difficulty_target_f64: MAX_DIFFICULTY_TARGET_AS_F64, past_median_time_window_size: MEDIAN_TIME_SAMPLED_WINDOW_SIZE as usize, difficulty_window_size: DIFFICULTY_SAMPLED_WINDOW_SIZE as usize, min_difficulty_window_size: MIN_DIFFICULTY_WINDOW_SIZE, coinbase_payload_script_public_key_max_len: 150, max_coinbase_payload_len: 204, // Limit the cost of calculating compute/transient/storage masses max_tx_inputs: 1000, max_tx_outputs: 1000, // Transient mass caps the entire block's transient footprint at 250KB, so no individual signature script can exceed it. max_signature_script_len: MAX_SIGNATURE_SCRIPT_LEN, // Retain a 10KB per-output guard; compute mass caps aggregate script-public-key bytes at roughly 45.5KB per block. // Note that storage mass will kick in and gradually penalize also for lower lengths (generalized KIP-0009, plurality will be high). max_script_public_key_len: 10_000, mass_per_tx_byte: 1, mass_per_script_pub_key_byte: 10, mass_per_sig_op: 1000, block_mass_limits: BlockMassLimits { compute: 500_000, storage: 500_000, transient: 1_000_000 }, block_lane_limits: BlockLaneLimits { lanes_per_block: DEFAULT_LANES_PER_BLOCK_LIMIT, gas_per_lane: DEFAULT_GAS_PER_LANE_LIMIT }, storage_mass_parameter: STORAGE_MASS_PARAMETER, // deflationary_phase_daa_score is the DAA score after which the pre-deflationary period // switches to the deflationary period. This number is calculated as follows: // We define a year as 365.25 days // Half a year in seconds = 365.25 / 2 * 24 * 60 * 60 = 15778800 // The network was down for three days shortly after launch // Three days in seconds = 3 * 24 * 60 * 60 = 259200 deflationary_phase_daa_score: 15778800 - 259200, pre_deflationary_phase_base_subsidy: 50000000000, skip_proof_of_work: false, max_block_level: 250, pruning_proof_m: 1000, blockrate: BlockrateParams::new::<10>(), pre_crescendo_target_time_per_block: 1000, // 18:30 UTC, March 6, 2025 crescendo_activation: ForkActivation::new(88_657_000), }; pub const SIMNET_PARAMS: Params = Params { dns_seeders: &[], net: NetworkId::new(NetworkType::Simnet), genesis: SIMNET_GENESIS, timestamp_deviation_tolerance: TIMESTAMP_DEVIATION_TOLERANCE, max_difficulty_target: MAX_DIFFICULTY_TARGET, max_difficulty_target_f64: MAX_DIFFICULTY_TARGET_AS_F64, past_median_time_window_size: MEDIAN_TIME_SAMPLED_WINDOW_SIZE as usize, difficulty_window_size: DIFFICULTY_SAMPLED_WINDOW_SIZE as usize, min_difficulty_window_size: MIN_DIFFICULTY_WINDOW_SIZE, deflationary_phase_daa_score: TenBps::deflationary_phase_daa_score(), pre_deflationary_phase_base_subsidy: TenBps::pre_deflationary_phase_base_subsidy(), coinbase_payload_script_public_key_max_len: 150, max_coinbase_payload_len: 204, max_tx_inputs: 1000, max_tx_outputs: 1000, max_signature_script_len: MAX_SIGNATURE_SCRIPT_LEN, max_script_public_key_len: 10_000, mass_per_tx_byte: 1, mass_per_script_pub_key_byte: 10, mass_per_sig_op: 1000, // Transient mass is increased for stark proofs block_mass_limits: BlockMassLimits { compute: 500_000, storage: 500_000, transient: 1_000_000 }, block_lane_limits: BlockLaneLimits { lanes_per_block: DEFAULT_LANES_PER_BLOCK_LIMIT, gas_per_lane: DEFAULT_GAS_PER_LANE_LIMIT }, storage_mass_parameter: STORAGE_MASS_PARAMETER, skip_proof_of_work: true, // For simnet only, PoW can be simulated by default max_block_level: 250, pruning_proof_m: PRUNING_PROOF_M, // For simnet, we deviate from default 10BPS configuration and allow at least 64 parents in order to support mempool benchmarks out of the box blockrate: BlockrateParams::new::<10>().increase_max_block_parents(64), pre_crescendo_target_time_per_block: TenBps::target_time_per_block(), crescendo_activation: ForkActivation::always(), }; pub const DEVNET_PARAMS: Params = Params { dns_seeders: &[], net: NetworkId::new(NetworkType::Devnet), genesis: DEVNET_GENESIS, timestamp_deviation_tolerance: TIMESTAMP_DEVIATION_TOLERANCE, max_difficulty_target: MAX_DIFFICULTY_TARGET, max_difficulty_target_f64: MAX_DIFFICULTY_TARGET_AS_F64, past_median_time_window_size: MEDIAN_TIME_SAMPLED_WINDOW_SIZE as usize, difficulty_window_size: DIFFICULTY_SAMPLED_WINDOW_SIZE as usize, min_difficulty_window_size: MIN_DIFFICULTY_WINDOW_SIZE, coinbase_payload_script_public_key_max_len: 150, max_coinbase_payload_len: 204, max_tx_inputs: 1000, max_tx_outputs: 1000, max_signature_script_len: MAX_SIGNATURE_SCRIPT_LEN, max_script_public_key_len: 10_000, mass_per_tx_byte: 1, mass_per_script_pub_key_byte: 10, mass_per_sig_op: 1000, // Transient mass is increased for stark proofs block_mass_limits: BlockMassLimits { compute: 500_000, storage: 500_000, transient: 1_000_000 }, block_lane_limits: BlockLaneLimits { lanes_per_block: DEFAULT_LANES_PER_BLOCK_LIMIT, gas_per_lane: DEFAULT_GAS_PER_LANE_LIMIT }, storage_mass_parameter: STORAGE_MASS_PARAMETER, deflationary_phase_daa_score: 0, pre_deflationary_phase_base_subsidy: TenBps::pre_deflationary_phase_base_subsidy(), skip_proof_of_work: false, max_block_level: 250, pruning_proof_m: 1000, blockrate: BlockrateParams::new::<10>(), pre_crescendo_target_time_per_block: TenBps::target_time_per_block(), crescendo_activation: ForkActivation::always(), }; #[cfg(test)] mod tests { use super::*; #[test] fn override_params_rejects_unknown_top_level_fields() { let err = serde_json::from_str::(r#"{"toccata_activation":42}"#).unwrap_err(); assert!(err.to_string().contains("unknown field `toccata_activation`"), "{err}"); } #[test] fn override_params_rejects_unknown_nested_blockrate_fields() { let err = serde_json::from_str::( r#"{ "blockrate": { "target_time_per_block": 100, "ghostdag_k": 124, "past_median_time_sample_rate": 10, "difficulty_sample_rate": 2, "max_block_parents": 16, "mergeset_size_limit": 248, "merge_depth": 36000, "finality_depth": 432000, "pruning_depth": 1080000, "coinbase_maturity": 200, "unexpected": 1 } }"#, ) .unwrap_err(); assert!(err.to_string().contains("unknown field `unexpected`"), "{err}"); } #[test] fn override_params_rejects_unknown_nested_mass_limit_fields() { let err = serde_json::from_str::( r#"{ "block_mass_limits": { "storage": 500000, "compute": 500000, "transient": 500000, "unexpected": 1 } }"#, ) .unwrap_err(); assert!(err.to_string().contains("unknown field `unexpected`"), "{err}"); } }