use crate::consensus::{
graph_weight, header_version, HeaderInfo, BASE_EDGE_BITS, BLOCK_TIME_SEC, C32_GRAPH_WEIGHT,
COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, DMA_WINDOW,
GRIN_BASE, INITIAL_DIFFICULTY, KERNEL_WEIGHT, MAX_BLOCK_WEIGHT, OUTPUT_WEIGHT, PROOFSIZE,
SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD,
};
use crate::core::block::HeaderVersion;
use crate::pow::{
self, new_cuckaroo_ctx, new_cuckarood_ctx, new_cuckaroom_ctx, new_cuckarooz_ctx,
new_cuckatoo_ctx, no_cuckaroo_ctx, BitVec, PoWContext,
};
use crate::ser::ProtocolVersion;
use std::cell::Cell;
use util::OneTime;
pub const PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion(1_000);
pub const AUTOMATED_TESTING_MIN_EDGE_BITS: u8 = 10;
pub const AUTOMATED_TESTING_PROOF_SIZE: usize = 8;
pub const USER_TESTING_MIN_EDGE_BITS: u8 = 15;
pub const USER_TESTING_PROOF_SIZE: usize = 42;
pub const AUTOMATED_TESTING_COINBASE_MATURITY: u64 = 3;
pub const USER_TESTING_COINBASE_MATURITY: u64 = 3;
pub const AUTOMATED_TESTING_CUT_THROUGH_HORIZON: u32 = 20;
pub const USER_TESTING_CUT_THROUGH_HORIZON: u32 = 70;
pub const TESTING_STATE_SYNC_THRESHOLD: u32 = 20;
pub const TESTING_INITIAL_DIFFICULTY: u64 = 1;
pub const TESTING_MAX_BLOCK_WEIGHT: u64 = 250;
pub const DEFAULT_ACCEPT_FEE_BASE: u64 = GRIN_BASE / 100 / 20;
pub const DEFAULT_FUTURE_TIME_LIMIT: u64 = 5 * 60;
pub const STUCK_PEER_KICK_TIME: i64 = 2 * 3600 * 1000;
const PEER_EXPIRATION_DAYS: i64 = 7 * 2;
pub const PEER_EXPIRATION_REMOVE_TIME: i64 = PEER_EXPIRATION_DAYS * 24 * 3600;
pub const COMPACTION_CHECK: u64 = DAY_HEIGHT;
pub const TESTING_TXHASHSET_ARCHIVE_INTERVAL: u64 = 10;
pub const TXHASHSET_ARCHIVE_INTERVAL: u64 = 12 * 60;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum ChainTypes {
AutomatedTesting,
UserTesting,
Testnet,
Mainnet,
}
impl ChainTypes {
pub fn shortname(&self) -> String {
match *self {
ChainTypes::AutomatedTesting => "auto".to_owned(),
ChainTypes::UserTesting => "user".to_owned(),
ChainTypes::Testnet => "test".to_owned(),
ChainTypes::Mainnet => "main".to_owned(),
}
}
}
impl Default for ChainTypes {
fn default() -> ChainTypes {
ChainTypes::Mainnet
}
}
lazy_static! {
pub static ref GLOBAL_CHAIN_TYPE: OneTime<ChainTypes> = OneTime::new();
pub static ref GLOBAL_ACCEPT_FEE_BASE: OneTime<u64> = OneTime::new();
pub static ref GLOBAL_FUTURE_TIME_LIMIT: OneTime<u64> = OneTime::new();
pub static ref GLOBAL_NRD_FEATURE_ENABLED: OneTime<bool> = OneTime::new();
}
thread_local! {
pub static CHAIN_TYPE: Cell<Option<ChainTypes>> = Cell::new(None);
pub static ACCEPT_FEE_BASE: Cell<Option<u64>> = Cell::new(None);
pub static FUTURE_TIME_LIMIT: Cell<Option<u64>> = Cell::new(None);
pub static NRD_FEATURE_ENABLED: Cell<Option<bool>> = Cell::new(None);
}
pub fn init_global_chain_type(new_type: ChainTypes) {
GLOBAL_CHAIN_TYPE.init(new_type)
}
pub fn set_local_chain_type(new_type: ChainTypes) {
CHAIN_TYPE.with(|chain_type| chain_type.set(Some(new_type)))
}
pub fn get_chain_type() -> ChainTypes {
CHAIN_TYPE.with(|chain_type| match chain_type.get() {
None => {
if !GLOBAL_CHAIN_TYPE.is_init() {
panic!("GLOBAL_CHAIN_TYPE and CHAIN_TYPE unset. Consider set_local_chain_type() in tests.");
}
let chain_type = GLOBAL_CHAIN_TYPE.borrow();
set_local_chain_type(chain_type);
chain_type
}
Some(chain_type) => chain_type,
})
}
pub fn init_global_future_time_limit(new_ftl: u64) {
GLOBAL_FUTURE_TIME_LIMIT.init(new_ftl)
}
pub fn init_global_accept_fee_base(new_base: u64) {
GLOBAL_ACCEPT_FEE_BASE.init(new_base)
}
pub fn set_local_accept_fee_base(new_base: u64) {
ACCEPT_FEE_BASE.with(|base| base.set(Some(new_base)))
}
pub fn get_accept_fee_base() -> u64 {
ACCEPT_FEE_BASE.with(|base| match base.get() {
None => {
let base = if GLOBAL_ACCEPT_FEE_BASE.is_init() {
GLOBAL_ACCEPT_FEE_BASE.borrow()
} else {
DEFAULT_ACCEPT_FEE_BASE
};
set_local_accept_fee_base(base);
base
}
Some(base) => base,
})
}
pub fn set_local_future_time_limit(new_ftl: u64) {
FUTURE_TIME_LIMIT.with(|ftl| ftl.set(Some(new_ftl)))
}
pub fn get_future_time_limit() -> u64 {
FUTURE_TIME_LIMIT.with(|ftl| match ftl.get() {
None => {
let ftl = if GLOBAL_FUTURE_TIME_LIMIT.is_init() {
GLOBAL_FUTURE_TIME_LIMIT.borrow()
} else {
DEFAULT_FUTURE_TIME_LIMIT
};
set_local_future_time_limit(ftl);
ftl
}
Some(ftl) => ftl,
})
}
pub fn init_global_nrd_enabled(enabled: bool) {
GLOBAL_NRD_FEATURE_ENABLED.init(enabled)
}
pub fn set_local_nrd_enabled(enabled: bool) {
NRD_FEATURE_ENABLED.with(|flag| flag.set(Some(enabled)))
}
pub fn is_nrd_enabled() -> bool {
NRD_FEATURE_ENABLED.with(|flag| match flag.get() {
None => {
if GLOBAL_NRD_FEATURE_ENABLED.is_init() {
let global_flag = GLOBAL_NRD_FEATURE_ENABLED.borrow();
flag.set(Some(global_flag));
global_flag
} else {
false
}
}
Some(flag) => flag,
})
}
pub fn create_pow_context<T>(
height: u64,
edge_bits: u8,
proof_size: usize,
max_sols: u32,
) -> Result<Box<dyn PoWContext>, pow::Error> {
let chain_type = get_chain_type();
if chain_type == ChainTypes::Mainnet || chain_type == ChainTypes::Testnet {
if edge_bits > 29 {
new_cuckatoo_ctx(edge_bits, proof_size, max_sols)
} else {
match header_version(height) {
HeaderVersion(1) => new_cuckaroo_ctx(edge_bits, proof_size),
HeaderVersion(2) => new_cuckarood_ctx(edge_bits, proof_size),
HeaderVersion(3) => new_cuckaroom_ctx(edge_bits, proof_size),
HeaderVersion(4) => new_cuckarooz_ctx(edge_bits, proof_size),
_ => no_cuckaroo_ctx(),
}
}
} else {
new_cuckatoo_ctx(edge_bits, proof_size, max_sols)
}
}
pub fn min_edge_bits() -> u8 {
match get_chain_type() {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
_ => DEFAULT_MIN_EDGE_BITS,
}
}
pub fn base_edge_bits() -> u8 {
match get_chain_type() {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
_ => BASE_EDGE_BITS,
}
}
pub fn proofsize() -> usize {
match get_chain_type() {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_PROOF_SIZE,
ChainTypes::UserTesting => USER_TESTING_PROOF_SIZE,
_ => PROOFSIZE,
}
}
pub fn coinbase_maturity() -> u64 {
match get_chain_type() {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_COINBASE_MATURITY,
ChainTypes::UserTesting => USER_TESTING_COINBASE_MATURITY,
_ => COINBASE_MATURITY,
}
}
pub fn initial_block_difficulty() -> u64 {
match get_chain_type() {
ChainTypes::AutomatedTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::UserTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::Testnet => INITIAL_DIFFICULTY,
ChainTypes::Mainnet => INITIAL_DIFFICULTY,
}
}
pub fn initial_graph_weight() -> u32 {
match get_chain_type() {
ChainTypes::AutomatedTesting => graph_weight(0, AUTOMATED_TESTING_MIN_EDGE_BITS) as u32,
ChainTypes::UserTesting => graph_weight(0, USER_TESTING_MIN_EDGE_BITS) as u32,
ChainTypes::Testnet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
ChainTypes::Mainnet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
}
}
pub fn min_wtema_graph_weight() -> u64 {
match get_chain_type() {
ChainTypes::AutomatedTesting => graph_weight(0, AUTOMATED_TESTING_MIN_EDGE_BITS),
ChainTypes::UserTesting => graph_weight(0, USER_TESTING_MIN_EDGE_BITS),
ChainTypes::Testnet => graph_weight(0, SECOND_POW_EDGE_BITS),
ChainTypes::Mainnet => C32_GRAPH_WEIGHT,
}
}
pub fn max_block_weight() -> u64 {
match get_chain_type() {
ChainTypes::AutomatedTesting => TESTING_MAX_BLOCK_WEIGHT,
ChainTypes::UserTesting => TESTING_MAX_BLOCK_WEIGHT,
ChainTypes::Testnet => MAX_BLOCK_WEIGHT,
ChainTypes::Mainnet => MAX_BLOCK_WEIGHT,
}
}
pub fn max_tx_weight() -> u64 {
let coinbase_weight = OUTPUT_WEIGHT + KERNEL_WEIGHT;
max_block_weight().saturating_sub(coinbase_weight) as u64
}
pub fn cut_through_horizon() -> u32 {
match get_chain_type() {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_CUT_THROUGH_HORIZON,
ChainTypes::UserTesting => USER_TESTING_CUT_THROUGH_HORIZON,
_ => CUT_THROUGH_HORIZON,
}
}
pub fn state_sync_threshold() -> u32 {
match get_chain_type() {
ChainTypes::AutomatedTesting => TESTING_STATE_SYNC_THRESHOLD,
ChainTypes::UserTesting => TESTING_STATE_SYNC_THRESHOLD,
_ => STATE_SYNC_THRESHOLD,
}
}
pub fn txhashset_archive_interval() -> u64 {
match get_chain_type() {
ChainTypes::AutomatedTesting => TESTING_TXHASHSET_ARCHIVE_INTERVAL,
ChainTypes::UserTesting => TESTING_TXHASHSET_ARCHIVE_INTERVAL,
_ => TXHASHSET_ARCHIVE_INTERVAL,
}
}
pub fn is_production_mode() -> bool {
match get_chain_type() {
ChainTypes::Testnet => true,
ChainTypes::Mainnet => true,
_ => false,
}
}
pub fn is_testnet() -> bool {
match get_chain_type() {
ChainTypes::Testnet => true,
_ => false,
}
}
pub fn difficulty_data_to_vector<T>(cursor: T) -> Vec<HeaderInfo>
where
T: IntoIterator<Item = HeaderInfo>,
{
let needed_block_count = DMA_WINDOW as usize + 1;
let mut last_n: Vec<HeaderInfo> = cursor.into_iter().take(needed_block_count).collect();
let n = last_n.len();
if needed_block_count > n {
let last_ts_delta = if n > 1 {
last_n[0].timestamp - last_n[1].timestamp
} else {
BLOCK_TIME_SEC
};
let last_diff = last_n[0].difficulty;
let mut last_ts = last_n.last().unwrap().timestamp;
for _ in n..needed_block_count {
last_ts = last_ts.saturating_sub(last_ts_delta);
last_n.push(HeaderInfo::from_ts_diff(last_ts, last_diff));
}
}
last_n.reverse();
last_n
}
#[inline]
pub fn header_size_bytes(edge_bits: u8) -> usize {
let size = 2 + 2 * 8 + 5 * 32 + 32 + 2 * 8;
let proof_size = 8 + 4 + 8 + 1 + BitVec::bytes_len(edge_bits as usize * proofsize());
size + proof_size
}
#[cfg(test)]
mod test {
use super::*;
use crate::core::Block;
use crate::genesis::*;
use crate::pow::mine_genesis_block;
use crate::ser::{BinWriter, Writeable};
fn test_header_len(genesis: Block) {
let mut raw = Vec::<u8>::with_capacity(1_024);
let mut writer = BinWriter::new(&mut raw, ProtocolVersion::local());
genesis.header.write(&mut writer).unwrap();
assert_eq!(raw.len(), header_size_bytes(genesis.header.pow.edge_bits()));
}
#[test]
fn automated_testing_header_len() {
set_local_chain_type(ChainTypes::AutomatedTesting);
test_header_len(mine_genesis_block().unwrap());
}
#[test]
fn user_testing_header_len() {
set_local_chain_type(ChainTypes::UserTesting);
test_header_len(mine_genesis_block().unwrap());
}
#[test]
fn testnet_header_len() {
set_local_chain_type(ChainTypes::Testnet);
test_header_len(genesis_test());
}
#[test]
fn mainnet_header_len() {
set_local_chain_type(ChainTypes::Mainnet);
test_header_len(genesis_main());
}
}