use crate::consensus;
use crate::consensus::HeaderInfo;
use crate::consensus::{
graph_weight, BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON,
DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, INITIAL_DIFFICULTY,
MAX_BLOCK_WEIGHT, PROOFSIZE, SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD,
};
use crate::core::block::feijoada::{AllowPolicy, Policy, PolicyConfig};
use crate::pow::{self, new_cuckatoo_ctx, EdgeType, PoWContext};
use crate::util::RwLock;
use sha2::{Digest, Sha256};
use std::env;
use std::fs::File;
use std::path::Path;
pub const PROTOCOL_VERSION: u32 = 2;
pub const AUTOMATED_TESTING_MIN_EDGE_BITS: u8 = 9;
pub const AUTOMATED_TESTING_PROOF_SIZE: usize = 4;
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 FLOONET_COINBASE_MATURITY: u64 = 30;
pub const TESTING_CUT_THROUGH_HORIZON: u32 = 70;
pub const TESTING_STATE_SYNC_THRESHOLD: u32 = 20;
pub const TESTING_INITIAL_GRAPH_WEIGHT: u32 = 1;
pub const TESTING_INITIAL_DIFFICULTY: u64 = 1;
pub const TESTING_MAX_BLOCK_WEIGHT: usize = 150;
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;
pub const CURRENT_HEADER_VERSION: u16 = 6;
#[cfg(target_family = "unix")]
pub const MAINNET_FOUNDATION_JSON_SHA256: &str =
"5a3a7584127dd31fba18eaeff1c551bfaa74b4e50e537a1e1904fe6730b17f5c";
#[cfg(target_family = "windows")]
pub const MAINNET_FOUNDATION_JSON_SHA256: &str =
"8ef0a84b35ec04576e583b7ed2f8a0d1becf4ee6ce67f9f3608deff8ad2ad103";
#[cfg(target_family = "unix")]
pub const FLOONET_FOUNDATION_JSON_SHA256: &str =
"503a4d5ccf214df86722d14cc93c1779c54e5b827773c8a2f65888a06f2efbad";
#[cfg(target_family = "windows")]
pub const FLOONET_FOUNDATION_JSON_SHA256: &str =
"503a4d5ccf214df86722d14cc93c1779c54e5b827773c8a2f65888a06f2efbad";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ChainTypes {
AutomatedTesting,
UserTesting,
Floonet,
Mainnet,
}
impl ChainTypes {
pub fn shortname(&self) -> String {
match *self {
ChainTypes::AutomatedTesting => "auto".to_owned(),
ChainTypes::UserTesting => "user".to_owned(),
ChainTypes::Floonet => "floo".to_owned(),
ChainTypes::Mainnet => "main".to_owned(),
}
}
}
impl Default for ChainTypes {
fn default() -> ChainTypes {
ChainTypes::Mainnet
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PoWContextTypes {
Cuckoo,
Cuckatoo,
Cuckaroo,
}
lazy_static! {
pub static ref CHAIN_TYPE: RwLock<ChainTypes> =
RwLock::new(ChainTypes::Mainnet);
pub static ref POW_CONTEXT_TYPE: RwLock<PoWContextTypes> =
RwLock::new(PoWContextTypes::Cuckoo);
pub static ref POLICY_CONFIG : RwLock<PolicyConfig> =
RwLock::new(PolicyConfig::default());
pub static ref FOUNDATION_FILE : RwLock<Option<String>> =
RwLock::new(None);
pub static ref EPIC_VERSION : RwLock<Option<Version>> =
RwLock::new(None);
pub static ref HEADER_SYNC_TIMEOUT : RwLock<i64> =
RwLock::new(10);
}
pub fn foundation_json_sha256() -> &'static str {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::Mainnet => MAINNET_FOUNDATION_JSON_SHA256,
_ => FLOONET_FOUNDATION_JSON_SHA256,
}
}
pub fn get_header_sync_timeout() -> i64 {
let header_sync_timeout = HEADER_SYNC_TIMEOUT.read();
header_sync_timeout.clone()
}
pub fn set_header_sync_timeout(timeout: i64) {
let mut header_sync_timeout = HEADER_SYNC_TIMEOUT.write();
*header_sync_timeout = if timeout <= 0 { 10 } else { timeout }
}
pub fn set_epic_version(version_major: String, version_minor: String) {
let mut epic_version = EPIC_VERSION.write();
let major_int: u32 = version_major.parse().expect("The current version of the epic node in the Cargo.toml is not valid! The major release value should be an integer");
let minor_int: u32 = version_minor.parse().expect("The current version of the epic node in the Cargo.toml is not valid! The minor release value should be an integer");
*epic_version = Some(Version::new(major_int, minor_int));
}
pub fn get_epic_version() -> Option<Version> {
let epic_version = EPIC_VERSION.read();
epic_version.clone()
}
pub fn set_foundation_path(path: String) {
let mut foundation_path = FOUNDATION_FILE.write();
let path_str = use_alternative_path(path);
*foundation_path = Some(path_str);
}
pub fn use_alternative_path(path_str: String) -> String {
let check_path = Path::new(&path_str);
if check_path.exists() {
if check_foundation(path_str.clone()) {
return path_str;
};
};
let mut p = env::current_exe().expect(
"Failed to get the executable's directory and no path to the foundation.json was provided!",
);
if p.file_stem()
.unwrap()
.to_str()
.unwrap()
.contains(&"cucumber")
{
return path_str;
}
for _ in 0..3 {
p.pop();
}
p.push("debian");
let foundation_name = match CHAIN_TYPE.read().clone() {
ChainTypes::Mainnet => "foundation.json",
_ => "foundation_floonet.json",
};
p.push(foundation_name);
if check_path.exists() {
warn!(
"Invalid foundation file!\nCheck if the file `{}` was not changed!",
check_path.display()
);
println!(
"Invalid foundation file!\nCheck if the file `{}` was not changed!",
check_path.display()
);
} else {
warn!("The file `{}` was not found!", check_path.display());
println!("The file `{}` was not found!", check_path.display());
}
warn!("Will try to use the alternative file `{}`!", p.display());
println!("Will try to use the alternative file `{}`!", p.display());
return p.to_str().expect("Failed to get the executable's directory and no path to the foundation.json was provided!").to_owned();
}
pub fn get_foundation_path() -> Option<String> {
let foundation_path = FOUNDATION_FILE.read();
foundation_path.clone()
}
pub fn check_foundation(path_str: String) -> bool {
let hash_to_compare = foundation_json_sha256();
let hash = get_file_sha256(&path_str);
println!(
"################ foundation sha: {:?},{:?}",
path_str, hash_to_compare
);
println!("################ file sha: {:?}", hash);
if hash.as_str() != hash_to_compare {
false
} else {
true
}
}
pub fn set_policy_config(policy: PolicyConfig) {
let mut policy_config = POLICY_CONFIG.write();
*policy_config = policy;
}
pub fn add_allowed_policy(height: u64, value: u64) {
let mut policy_config = POLICY_CONFIG.write();
policy_config
.allowed_policies
.push(AllowPolicy { height, value });
}
pub fn get_allowed_policies() -> Vec<AllowPolicy> {
let policy_config = POLICY_CONFIG.read();
policy_config.allowed_policies.clone()
}
pub fn get_emitted_policy(height: u64) -> u8 {
let _policy_config = POLICY_CONFIG.read();
if height <= consensus::BLOCK_ERA_1 {
0
} else if height <= consensus::BLOCK_ERA_2 {
1
} else if height <= consensus::BLOCK_ERA_3 {
2
} else if height <= consensus::BLOCK_ERA_4 {
3
} else if height <= consensus::BLOCK_ERA_5 {
4
} else {
5
}
}
pub fn get_policies(index: u8) -> Option<Policy> {
let policy_config = POLICY_CONFIG.read();
if (index as usize) < (*policy_config).policies.len() {
Some(policy_config.policies[index as usize].clone())
} else {
None
}
}
pub fn get_policy_config() -> PolicyConfig {
let policy_config = POLICY_CONFIG.read();
policy_config.clone()
}
pub fn set_mining_mode(mode: ChainTypes) {
let mut param_ref = CHAIN_TYPE.write();
*param_ref = mode;
}
pub fn create_pow_context<T>(
_height: u64,
edge_bits: u8,
proof_size: usize,
max_sols: u32,
) -> Result<Box<dyn PoWContext<T>>, pow::Error>
where
T: EdgeType + 'static,
{
let chain_type = CHAIN_TYPE.read().clone();
match chain_type {
ChainTypes::Mainnet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
ChainTypes::Floonet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
_ => new_cuckatoo_ctx(edge_bits, proof_size, max_sols),
}
}
pub fn min_edge_bits() -> u8 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
_ => DEFAULT_MIN_EDGE_BITS,
}
}
pub fn base_edge_bits() -> u8 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS,
ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS,
_ => BASE_EDGE_BITS,
}
}
pub fn proofsize() -> usize {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_PROOF_SIZE,
ChainTypes::UserTesting => USER_TESTING_PROOF_SIZE,
_ => PROOFSIZE,
}
}
pub fn coinbase_maturity() -> u64 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => AUTOMATED_TESTING_COINBASE_MATURITY,
ChainTypes::UserTesting => USER_TESTING_COINBASE_MATURITY,
ChainTypes::Floonet => FLOONET_COINBASE_MATURITY,
_ => COINBASE_MATURITY,
}
}
pub fn initial_block_difficulty() -> u64 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::UserTesting => TESTING_INITIAL_DIFFICULTY,
ChainTypes::Floonet => INITIAL_DIFFICULTY,
ChainTypes::Mainnet => INITIAL_DIFFICULTY,
}
}
pub fn initial_graph_weight() -> u32 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::UserTesting => TESTING_INITIAL_GRAPH_WEIGHT,
ChainTypes::Floonet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
ChainTypes::Mainnet => graph_weight(0, SECOND_POW_EDGE_BITS) as u32,
}
}
pub fn max_block_weight() -> usize {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_MAX_BLOCK_WEIGHT,
ChainTypes::UserTesting => TESTING_MAX_BLOCK_WEIGHT,
ChainTypes::Floonet => MAX_BLOCK_WEIGHT,
ChainTypes::Mainnet => MAX_BLOCK_WEIGHT,
}
}
pub fn cut_through_horizon() -> u32 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_CUT_THROUGH_HORIZON,
ChainTypes::UserTesting => TESTING_CUT_THROUGH_HORIZON,
_ => CUT_THROUGH_HORIZON,
}
}
pub fn state_sync_threshold() -> u32 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_STATE_SYNC_THRESHOLD,
ChainTypes::UserTesting => TESTING_STATE_SYNC_THRESHOLD,
_ => STATE_SYNC_THRESHOLD,
}
}
pub fn is_automated_testing_mode() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::AutomatedTesting == *param_ref
}
pub fn is_user_testing_mode() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::UserTesting == *param_ref
}
pub fn txhashset_archive_interval() -> u64 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => TESTING_TXHASHSET_ARCHIVE_INTERVAL,
ChainTypes::UserTesting => TESTING_TXHASHSET_ARCHIVE_INTERVAL,
_ => TXHASHSET_ARCHIVE_INTERVAL,
}
}
pub fn is_production_mode() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::Floonet == *param_ref || ChainTypes::Mainnet == *param_ref
}
pub fn is_floonet() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::Floonet == *param_ref
}
pub fn is_mainnet() -> bool {
let param_ref = CHAIN_TYPE.read();
ChainTypes::Mainnet == *param_ref
}
pub fn get_genesis_nonce() -> u64 {
let param_ref = CHAIN_TYPE.read();
match *param_ref {
ChainTypes::AutomatedTesting => 0,
ChainTypes::UserTesting => 27944,
ChainTypes::Floonet => 0,
ChainTypes::Mainnet => 0,
}
}
pub fn chain_shortname() -> String {
let param_ref = CHAIN_TYPE.read();
param_ref.shortname()
}
pub fn difficulty_data_to_vector<T>(cursor: T, needed_block_count: u64) -> Vec<HeaderInfo>
where
T: IntoIterator<Item = HeaderInfo>,
{
let needed_block_count = needed_block_count as usize + 1;
let mut last_n: Vec<HeaderInfo> = cursor.into_iter().take(needed_block_count).collect();
for i in 1..last_n.len() {
last_n[i].timestamp = last_n[i - 1]
.timestamp
.saturating_sub(last_n[i - 1].prev_timespan);
}
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.clone();
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.clone()));
}
}
last_n.reverse();
last_n
}
pub fn ts_data_to_vector<T>(cursor: T, needed_block_count: u64) -> Vec<HeaderInfo>
where
T: IntoIterator<Item = HeaderInfo>,
{
let needed_block_count = needed_block_count as usize + 1;
let mut last_n: Vec<HeaderInfo> = cursor.into_iter().take(needed_block_count).collect();
for i in 1..last_n.len() {
last_n[i].timestamp = last_n[i - 1]
.timestamp
.saturating_sub(last_n[i - 1].prev_timespan);
}
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.clone();
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.clone()));
}
}
last_n
}
#[derive(Debug, Clone)]
pub struct Version {
pub version_major: u32,
pub version_minor: u32,
}
impl Version {
pub fn new(version_major: u32, version_minor: u32) -> Version {
Version {
version_major,
version_minor,
}
}
}
pub fn get_file_sha256(path: &str) -> String {
let file_open = File::open(path);
if file_open.is_err() {
println!(
"\nCouldn't open the foundation file!\nCheck if the file \"{}\" was not changed!",
path
);
};
let mut file = file_open.expect(
format!(
"Error trying to read the foundation.json. Couldn't find/open the file {}!",
path
)
.as_str(),
);
let mut sha256 = Sha256::new();
std::io::copy(&mut file, &mut sha256).expect(
format!(
"Error trying to read the foundation.json. Couldn't find/open the file {}!",
path
)
.as_str(),
);
let hash = sha256.result();
format!("{:x}", hash)
}