use super::u256::U256;
pub const HEADER_BYTES: usize = 168;
pub const BLOCK_TIME: i64 = 30;
pub const HALF_LIFE: i64 = 600;
pub const MTP_WINDOW: usize = 11;
pub const MAX_FUTURE_DRIFT: u64 = 120;
pub const POW_LIMIT_BITS: u32 = 0x1e10_0000;
pub const GENESIS_BITS: u32 = 0x1e01_0000;
pub const MAX_BLOCK_BYTES: usize = 262_144;
pub const MAX_BLOCK_TXS: usize = 1_024;
pub const MAX_TX_BYTES: usize = 512;
pub const MIN_FEE_PER_BYTE: u64 = 1;
pub const COINBASE_MATURITY: u64 = 100;
pub const FINALITY_DEPTH: u64 = 120;
pub const CHECKPOINT_INTERVAL: u64 = 120;
pub const CHECKPOINT_SUNSET: u64 = 525_600;
pub const BLOCKS_PER_YEAR: u64 = 1_051_200;
pub const SUBSIDY_DIVISOR: u64 = 105_120_000;
pub const ORPHAN_POOL_HEADERS: usize = 1_024;
pub const ORPHAN_POOL_SECONDS: u64 = 600;
pub const NONCE_WINDOW: u64 = 16;
pub const MAX_REORG: u64 = FINALITY_DEPTH - 1;
pub const EPOCH_LENGTH: u64 = 240;
pub const SETTLE_DELAY: u64 = FINALITY_DEPTH;
pub const MIN_LOCK: u64 = 20_160;
pub const FOCUS_MAX_BPS: u64 = 5_000;
pub const POOL_RELEASE: u64 = 4;
pub const R_MAX_PPM: u64 = 10_000;
pub const HWM_KEEP_NUM: u64 = 65_410;
pub const HWM_KEEP_DEN: u64 = 65_536;
pub const PHI_STEPS: usize = 48;
pub const ATTR_NODES: usize = 4;
pub const SETTLE_BUDGET_SECS: u64 = 900;
pub const RHO_LAUNCH: u64 = 1;
pub const KARMA_LAUNCH: u64 = 1;
pub const DOMAIN_CARD: &[u8] = b"soft3/chain/card/v1\0";
pub const DOMAIN_STAKE: &[u8] = b"soft3/chain/stake/v1\0";
pub const DOMAIN_FOCUS: &[u8] = b"soft3/chain/focus/v1\0";
pub const DOMAIN_PHI: &[u8] = b"soft3/chain/phi/v1\0";
pub const DOMAIN_EPOCH: &[u8] = b"soft3/chain/epoch/v1\0";
pub const DOMAIN_PAYOUT_LEAF: &[u8] = b"soft3/chain/payout-leaf/v1\0";
pub const DOMAIN_PAYOUT_NODE: &[u8] = b"soft3/chain/payout-node/v1\0";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EpochClock {
pub length: u64,
pub delay: u64,
pub min_lock: u64,
}
impl EpochClock {
pub const SPEC: EpochClock = EpochClock { length: EPOCH_LENGTH, delay: SETTLE_DELAY, min_lock: MIN_LOCK };
pub fn valid(&self) -> bool {
self.delay >= 1 && self.length > self.delay && self.min_lock >= self.length + self.delay
}
pub fn epoch(&self, h: u64) -> u64 {
h.div_ceil(self.length)
}
pub fn boundary(&self, e: u64) -> u64 {
e * self.length
}
pub fn is_boundary(&self, h: u64) -> bool {
h % self.length == 0
}
pub fn settle(&self, e: u64) -> u64 {
self.boundary(e) + self.delay
}
pub fn settles_at(&self, h: u64) -> Option<u64> {
(h > self.delay && (h - self.delay) % self.length == 0).then(|| (h - self.delay) / self.length).filter(|e| *e >= 1)
}
pub fn from_env() -> Option<EpochClock> {
let v = std::env::var("SOFT3_DEV_EPOCH").ok()?;
let mut it = v.split(':').map(|x| x.trim().parse::<u64>());
let (Some(Ok(length)), Some(Ok(delay)), Some(Ok(min_lock))) = (it.next(), it.next(), it.next()) else { return None };
let c = EpochClock { length, delay, min_lock };
c.valid().then_some(c)
}
}
pub const RULE_SET: u16 = 1;
pub const ACTIVATIONS: &[(u16, u64)] = &[(RULE_SET, 0)];
pub fn rule_set_at(height: u64) -> Option<u16> {
ACTIVATIONS
.iter()
.filter(|(_, h)| *h <= height)
.map(|(r, _)| *r)
.max()
}
pub const GENESIS_DIALECT: &str = "urn:cyberia:chain-genesis:1";
pub const DOMAIN_LEAF: &[u8] = b"soft3/chain/leaf/v1\0";
pub const DOMAIN_NODE: &[u8] = b"soft3/chain/node/v1\0";
pub const DOMAIN_TXID: &[u8] = b"soft3/chain/txid/v1\0";
pub const DOMAIN_SIGN: &[u8] = b"soft3/chain/sign/v1\0";
pub const DOMAIN_LEGACY: &[u8] = b"soft3/chain/legacy/v1\0";
pub const DOMAIN_CHECKPOINT: &[u8] = b"soft3/chain/checkpoint/v1\0";
pub const DOMAIN_TOPIC: &[u8] = b"soft3/chain/topic/v1\0";
pub fn pow_limit() -> U256 {
#[cfg(test)]
if let Some(limit) = test_pow::LIMIT.with(|l| l.get()) {
return limit;
}
#[cfg(feature = "sim")]
if sim_pow::relaxed() {
return U256::ONE << 255;
}
U256::ONE << 236
}
#[cfg(feature = "sim")]
pub mod sim_pow {
use std::sync::atomic::{AtomicBool, Ordering};
static RELAXED: AtomicBool = AtomicBool::new(false);
pub fn relax() {
RELAXED.store(true, Ordering::SeqCst);
}
pub fn relaxed() -> bool {
RELAXED.load(Ordering::SeqCst)
}
pub const EASY_BITS: u32 = 0x2100_8000;
}
#[cfg(test)]
pub mod test_pow {
use super::U256;
use std::cell::Cell;
thread_local! {
pub static LIMIT: Cell<Option<U256>> = const { Cell::new(None) };
}
pub fn relax() {
LIMIT.with(|l| l.set(Some(U256::ONE << 255)));
}
pub const EASY_BITS: u32 = 0x2100_8000;
}
pub fn genesis_target() -> U256 {
U256::ONE << 232
}
pub fn subsidy(s0: u64) -> u64 {
s0 / SUBSIDY_DIVISOR
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EmbeddedChain {
pub name: &'static str,
pub hrp: &'static str,
pub chain_id: [u8; 32],
pub genesis_block: [u8; 32],
pub genesis_time: u64,
}
pub const CHAINS: &[EmbeddedChain] = &[
EmbeddedChain {
name: "pussy",
hrp: "pussy",
chain_id: [0; 32],
genesis_block: [0; 32],
genesis_time: 1_793_885_820,
},
EmbeddedChain {
name: "bostrom",
hrp: "bostrom",
chain_id: [0; 32],
genesis_block: [0; 32],
genesis_time: 1_793_884_962,
},
];
pub fn embedded(name: &str) -> Option<&'static EmbeddedChain> {
CHAINS.iter().find(|c| c.name == name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn subsidy_is_one_percent_per_year_floored() {
assert_eq!(subsidy(105_120_000), 1);
assert_eq!(subsidy(105_119_999), 0);
assert_eq!(subsidy(1_152_075_800_167_936_256), 10_959_625_191);
assert_eq!(BLOCKS_PER_YEAR * 100, SUBSIDY_DIVISOR);
}
#[test]
fn rule_set_table_starts_at_genesis() {
assert_eq!(rule_set_at(0), Some(1));
assert_eq!(rule_set_at(u64::MAX), Some(1));
assert_eq!(MAX_REORG, 119);
}
#[test]
fn epoch_clock_matches_chain_economy_section_10() {
let c = EpochClock::SPEC;
assert!(c.valid());
assert_eq!((c.epoch(0), c.epoch(1), c.epoch(240), c.epoch(241)), (0, 1, 1, 2));
assert_eq!(c.settle(1), 360);
assert_eq!(c.settles_at(360), Some(1));
assert_eq!(c.settles_at(600), Some(2));
assert_eq!((c.settles_at(120), c.settles_at(359), c.settles_at(240)), (None, None, None));
assert!(MIN_LOCK >= EPOCH_LENGTH + SETTLE_DELAY);
assert!(!EpochClock { length: 10, delay: 10, min_lock: 20 }.valid());
assert!(!EpochClock { length: 10, delay: 5, min_lock: 14 }.valid());
assert!(EpochClock { length: 10, delay: 5, min_lock: 15 }.valid());
}
#[test]
fn embedded_chains_have_the_launch_times() {
assert_eq!(embedded("pussy").unwrap().genesis_time, 1_793_885_820);
assert_eq!(embedded("bostrom").unwrap().genesis_time, 1_793_884_962);
assert!(embedded("spacepussy-test").is_none());
}
}