1use std::str::FromStr;
5use std::sync::LazyLock;
6
7use ahash::HashMap;
8use cid::Cid;
9use fvm_ipld_blockstore::Blockstore;
10use itertools::Itertools;
11use libp2p::Multiaddr;
12use num_traits::Zero;
13use serde::{Deserialize, Serialize};
14use strum::IntoEnumIterator;
15use strum_macros::Display;
16use strum_macros::EnumIter;
17use tracing::warn;
18
19use crate::beacon::{BeaconPoint, BeaconSchedule, DrandBeacon, DrandConfig};
20use crate::db::SettingsStore;
21use crate::eth::EthChainId;
22use crate::shim::{
23 clock::{ChainEpoch, EPOCH_DURATION_SECONDS, EPOCHS_IN_DAY},
24 econ::TokenAmount,
25 machine::BuiltinActorManifest,
26 runtime::Policy,
27 sector::{RegisteredPoStProofV3, RegisteredSealProofV3},
28 version::NetworkVersion,
29};
30use crate::utils::misc::env::env_or_default;
31use crate::{make_butterfly_policy, make_calibnet_policy, make_devnet_policy, make_mainnet_policy};
32
33pub use network_name::{GenesisNetworkName, StateNetworkName};
34
35mod actors_bundle;
36pub use actors_bundle::{
37 ACTOR_BUNDLES, ACTOR_BUNDLES_METADATA, ActorBundleInfo, ActorBundleMetadata,
38 generate_actor_bundle, get_actor_bundles_metadata,
39};
40
41mod drand;
42
43pub mod network_name;
44
45pub mod butterflynet;
46pub mod calibnet;
47pub mod devnet;
48pub mod mainnet;
49
50pub mod metrics;
51
52pub const NEWEST_NETWORK_VERSION: NetworkVersion = NetworkVersion::V25;
54
55const ENV_FOREST_BLOCK_DELAY_SECS: &str = "FOREST_BLOCK_DELAY_SECS";
56const ENV_FOREST_PROPAGATION_DELAY_SECS: &str = "FOREST_PROPAGATION_DELAY_SECS";
57const ENV_PLEDGE_RULE_RAMP: &str = "FOREST_PLEDGE_RULE_RAMP";
58
59static INITIAL_FIL_RESERVED: LazyLock<TokenAmount> =
60 LazyLock::new(|| TokenAmount::from_whole(300_000_000));
61
62#[derive(
65 Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash, derive_more::Display,
66)]
67#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
68#[serde(tag = "type", content = "name", rename_all = "lowercase")]
69#[display(rename_all = "lowercase")]
70pub enum NetworkChain {
71 #[default]
72 Mainnet,
73 Calibnet,
74 Butterflynet,
75 Devnet(String),
76}
77
78impl FromStr for NetworkChain {
79 type Err = anyhow::Error;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 match s {
83 mainnet::NETWORK_COMMON_NAME | mainnet::NETWORK_GENESIS_NAME => {
84 Ok(NetworkChain::Mainnet)
85 }
86 calibnet::NETWORK_COMMON_NAME | calibnet::NETWORK_GENESIS_NAME => {
87 Ok(NetworkChain::Calibnet)
88 }
89 butterflynet::NETWORK_COMMON_NAME => Ok(NetworkChain::Butterflynet),
90 name => Ok(NetworkChain::Devnet(name.to_owned())),
91 }
92 }
93}
94
95impl NetworkChain {
96 pub fn genesis_name(&self) -> GenesisNetworkName {
102 match self {
103 NetworkChain::Mainnet => mainnet::NETWORK_GENESIS_NAME.into(),
104 NetworkChain::Calibnet => calibnet::NETWORK_GENESIS_NAME.into(),
105 _ => self.to_string().into(),
106 }
107 }
108 pub fn from_genesis(cid: &Cid) -> Option<Self> {
111 if cid == &*mainnet::GENESIS_CID {
112 Some(Self::Mainnet)
113 } else if cid == &*calibnet::GENESIS_CID {
114 Some(Self::Calibnet)
115 } else if cid == &*butterflynet::GENESIS_CID {
116 Some(Self::Butterflynet)
117 } else {
118 None
119 }
120 }
121
122 pub fn from_genesis_or_devnet_placeholder(cid: &Cid) -> Self {
127 Self::from_genesis(cid).unwrap_or(Self::Devnet(String::from("devnet")))
128 }
129
130 pub fn is_testnet(&self) -> bool {
131 !matches!(self, NetworkChain::Mainnet)
132 }
133
134 pub fn is_devnet(&self) -> bool {
135 matches!(self, NetworkChain::Devnet(..))
136 }
137}
138
139#[derive(
141 Debug, Default, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, EnumIter,
142)]
143#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
144pub enum Height {
145 #[default]
146 Breeze,
147 Smoke,
148 Ignition,
149 Refuel,
150 Assembly,
151 Tape,
152 Liftoff,
153 Kumquat,
154 Calico,
155 Persian,
156 Orange,
157 Claus,
158 Trust,
159 Norwegian,
160 Turbo,
161 Hyperdrive,
162 Chocolate,
163 OhSnap,
164 Skyr,
165 Shark,
166 Hygge,
167 Lightning,
168 Thunder,
169 Watermelon,
170 WatermelonFix,
171 WatermelonFix2,
172 Dragon,
173 DragonFix,
174 Phoenix,
175 Waffle,
176 TukTuk,
177 Teep,
178 Tock,
179 TockFix,
180 GoldenWeek,
181}
182
183impl From<Height> for NetworkVersion {
184 fn from(height: Height) -> NetworkVersion {
185 match height {
186 Height::Breeze => NetworkVersion::V1,
187 Height::Smoke => NetworkVersion::V2,
188 Height::Ignition => NetworkVersion::V3,
189 Height::Refuel => NetworkVersion::V3,
190 Height::Assembly => NetworkVersion::V4,
191 Height::Tape => NetworkVersion::V5,
192 Height::Liftoff => NetworkVersion::V5,
193 Height::Kumquat => NetworkVersion::V6,
194 Height::Calico => NetworkVersion::V7,
195 Height::Persian => NetworkVersion::V8,
196 Height::Orange => NetworkVersion::V9,
197 Height::Claus => NetworkVersion::V9,
198 Height::Trust => NetworkVersion::V10,
199 Height::Norwegian => NetworkVersion::V11,
200 Height::Turbo => NetworkVersion::V12,
201 Height::Hyperdrive => NetworkVersion::V13,
202 Height::Chocolate => NetworkVersion::V14,
203 Height::OhSnap => NetworkVersion::V15,
204 Height::Skyr => NetworkVersion::V16,
205 Height::Shark => NetworkVersion::V17,
206 Height::Hygge => NetworkVersion::V18,
207 Height::Lightning => NetworkVersion::V19,
208 Height::Thunder => NetworkVersion::V20,
209 Height::Watermelon => NetworkVersion::V21,
210 Height::WatermelonFix => NetworkVersion::V21,
211 Height::WatermelonFix2 => NetworkVersion::V21,
212 Height::Dragon => NetworkVersion::V22,
213 Height::DragonFix => NetworkVersion::V22,
214 Height::Phoenix => NetworkVersion::V22,
215 Height::Waffle => NetworkVersion::V23,
216 Height::TukTuk => NetworkVersion::V24,
217 Height::Teep => NetworkVersion::V25,
218 Height::Tock => NetworkVersion::V26,
219 Height::TockFix => NetworkVersion::V26,
220 Height::GoldenWeek => NetworkVersion::V27,
221 }
222 }
223}
224
225#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
226#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
227pub struct HeightInfo {
228 pub epoch: ChainEpoch,
229 pub bundle: Option<Cid>,
230}
231
232pub struct HeightInfoWithActorManifest<'a> {
233 #[allow(dead_code)]
234 pub height: Height,
235 pub info: &'a HeightInfo,
236 pub manifest_cid: Cid,
237}
238
239impl<'a> HeightInfoWithActorManifest<'a> {
240 pub fn manifest(&self, store: &impl Blockstore) -> anyhow::Result<BuiltinActorManifest> {
241 BuiltinActorManifest::load_manifest(store, &self.manifest_cid)
242 }
243}
244
245#[derive(Clone)]
246struct DrandPoint<'a> {
247 pub height: ChainEpoch,
248 pub config: &'a LazyLock<DrandConfig<'a>>,
249}
250
251#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
253#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
254#[serde(default)]
255pub struct ChainConfig {
256 pub network: NetworkChain,
257 pub genesis_cid: Option<String>,
258 #[cfg_attr(test, arbitrary(gen(
259 |g: &mut quickcheck::Gen| {
260 let addr = std::net::Ipv4Addr::arbitrary(&mut *g);
261 let n = u8::arbitrary(g) as usize;
262 vec![addr.into(); n]
263 }
264 )))]
265 pub bootstrap_peers: Vec<Multiaddr>,
266 pub block_delay_secs: u32,
267 pub propagation_delay_secs: u32,
268 pub genesis_network: NetworkVersion,
269 pub height_infos: HashMap<Height, HeightInfo>,
270 #[cfg_attr(test, arbitrary(gen(|_g| Policy::default())))]
271 pub policy: Policy,
272 pub eth_chain_id: EthChainId,
273 pub breeze_gas_tamping_duration: i64,
274 pub fip0081_ramp_duration_epochs: u64,
276 pub upgrade_teep_initial_fil_reserved: Option<TokenAmount>,
278 pub f3_enabled: bool,
279 pub f3_consensus: bool,
281 pub f3_bootstrap_epoch: i64,
282 pub f3_initial_power_table: Option<Cid>,
283 pub enable_indexer: bool,
284 pub enable_receipt_event_caching: bool,
285 pub default_max_fee: TokenAmount,
286}
287
288impl ChainConfig {
289 pub fn mainnet() -> Self {
290 use mainnet::*;
291 Self {
292 network: NetworkChain::Mainnet,
293 genesis_cid: Some(GENESIS_CID.to_string()),
294 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
295 block_delay_secs: env_or_default(
296 ENV_FOREST_BLOCK_DELAY_SECS,
297 EPOCH_DURATION_SECONDS as u32,
298 ),
299 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 10),
300 genesis_network: GENESIS_NETWORK_VERSION,
301 height_infos: HEIGHT_INFOS.clone(),
302 policy: make_mainnet_policy!(v13).into(),
303 eth_chain_id: ETH_CHAIN_ID,
304 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
305 fip0081_ramp_duration_epochs: 365 * EPOCHS_IN_DAY as u64,
307 upgrade_teep_initial_fil_reserved: None,
308 f3_enabled: true,
309 f3_consensus: true,
310 f3_bootstrap_epoch: 4920480,
312 f3_initial_power_table: Some(
313 "bafy2bzacecklgxd2eksmodvhgurqvorkg3wamgqkrunir3al2gchv2cikgmbu"
314 .parse()
315 .expect("invalid f3_initial_power_table"),
316 ),
317 enable_indexer: false,
318 enable_receipt_event_caching: true,
319 default_max_fee: TokenAmount::zero(),
320 }
321 }
322
323 pub fn calibnet() -> Self {
324 use calibnet::*;
325 Self {
326 network: NetworkChain::Calibnet,
327 genesis_cid: Some(GENESIS_CID.to_string()),
328 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
329 block_delay_secs: env_or_default(
330 ENV_FOREST_BLOCK_DELAY_SECS,
331 EPOCH_DURATION_SECONDS as u32,
332 ),
333 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 10),
334 genesis_network: GENESIS_NETWORK_VERSION,
335 height_infos: HEIGHT_INFOS.clone(),
336 policy: make_calibnet_policy!(v13).into(),
337 eth_chain_id: ETH_CHAIN_ID,
338 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
339 fip0081_ramp_duration_epochs: 3 * EPOCHS_IN_DAY as u64,
341 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_200_000_000)),
343 f3_enabled: true,
346 f3_consensus: true,
347 f3_bootstrap_epoch: 3_451_774,
349 f3_initial_power_table: Some(
350 "bafy2bzacednijkh5dhb6jb7snxhhtjt7zuqaydlewoha3ordhy76dhgwtmptg"
351 .parse()
352 .expect("invalid f3_initial_power_table"),
353 ),
354 enable_indexer: false,
355 enable_receipt_event_caching: true,
356 default_max_fee: TokenAmount::zero(),
357 }
358 }
359
360 pub fn devnet() -> Self {
361 use devnet::*;
362 Self {
363 network: NetworkChain::Devnet("devnet".to_string()),
364 genesis_cid: None,
365 bootstrap_peers: Vec::new(),
366 block_delay_secs: env_or_default(ENV_FOREST_BLOCK_DELAY_SECS, 4),
367 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 1),
368 genesis_network: *GENESIS_NETWORK_VERSION,
369 height_infos: HEIGHT_INFOS.clone(),
370 policy: make_devnet_policy!(v13).into(),
371 eth_chain_id: ETH_CHAIN_ID,
372 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
373 fip0081_ramp_duration_epochs: env_or_default(ENV_PLEDGE_RULE_RAMP, 200),
375 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_400_000_000)),
377 f3_enabled: false,
378 f3_consensus: false,
379 f3_bootstrap_epoch: -1,
380 f3_initial_power_table: None,
381 enable_indexer: false,
382 enable_receipt_event_caching: true,
383 default_max_fee: TokenAmount::zero(),
384 }
385 }
386
387 pub fn butterflynet() -> Self {
388 use butterflynet::*;
389 Self {
390 network: NetworkChain::Butterflynet,
391 genesis_cid: Some(GENESIS_CID.to_string()),
392 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
393 block_delay_secs: env_or_default(
394 ENV_FOREST_BLOCK_DELAY_SECS,
395 EPOCH_DURATION_SECONDS as u32,
396 ),
397 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 6),
398 genesis_network: GENESIS_NETWORK_VERSION,
399 height_infos: HEIGHT_INFOS.clone(),
400 policy: make_butterfly_policy!(v13).into(),
401 eth_chain_id: ETH_CHAIN_ID,
402 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
403 fip0081_ramp_duration_epochs: env_or_default(
405 ENV_PLEDGE_RULE_RAMP,
406 365 * EPOCHS_IN_DAY as u64,
407 ),
408 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_600_000_000)),
410 f3_enabled: true,
411 f3_consensus: true,
412 f3_bootstrap_epoch: 1000,
413 f3_initial_power_table: None,
414 enable_indexer: false,
415 enable_receipt_event_caching: true,
416 default_max_fee: TokenAmount::zero(),
417 }
418 }
419
420 pub fn from_chain(network_chain: &NetworkChain) -> Self {
421 match network_chain {
422 NetworkChain::Mainnet => Self::mainnet(),
423 NetworkChain::Calibnet => Self::calibnet(),
424 NetworkChain::Butterflynet => Self::butterflynet(),
425 NetworkChain::Devnet(name) => Self {
426 network: NetworkChain::Devnet(name.clone()),
427 ..Self::devnet()
428 },
429 }
430 }
431
432 fn network_height(&self, epoch: ChainEpoch) -> Option<Height> {
433 self.height_infos
434 .iter()
435 .sorted_by_key(|(_, info)| info.epoch)
436 .rev()
437 .find(|(_, info)| epoch > info.epoch)
438 .map(|(height, _)| *height)
439 }
440
441 pub fn network_height_with_actor_bundle<'a>(
443 &'a self,
444 epoch: ChainEpoch,
445 ) -> Option<HeightInfoWithActorManifest<'a>> {
446 if let Some((height, info, manifest_cid)) = self
447 .height_infos
448 .iter()
449 .sorted_by_key(|(_, info)| info.epoch)
450 .rev()
451 .filter_map(|(height, info)| info.bundle.map(|bundle| (*height, info, bundle)))
452 .find(|(_, info, _)| epoch > info.epoch)
453 {
454 Some(HeightInfoWithActorManifest {
455 height,
456 info,
457 manifest_cid,
458 })
459 } else {
460 None
461 }
462 }
463
464 pub fn network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
467 self.network_height(epoch)
468 .map(NetworkVersion::from)
469 .unwrap_or(self.genesis_network_version())
470 .max(self.genesis_network)
471 }
472
473 pub fn network_version_revision(&self, epoch: ChainEpoch) -> i64 {
476 if let Some(height) = self.network_height(epoch) {
477 let nv = NetworkVersion::from(height);
478 if let Some(rev0_height) = Height::iter().find(|h| NetworkVersion::from(*h) == nv) {
479 return (height as i64) - (rev0_height as i64);
480 }
481 }
482 0
483 }
484
485 pub fn get_beacon_schedule(&self, genesis_ts: u64) -> BeaconSchedule {
486 let ds_iter = match self.network {
487 NetworkChain::Mainnet => mainnet::DRAND_SCHEDULE.iter(),
488 NetworkChain::Calibnet => calibnet::DRAND_SCHEDULE.iter(),
489 NetworkChain::Butterflynet => butterflynet::DRAND_SCHEDULE.iter(),
490 NetworkChain::Devnet(_) => devnet::DRAND_SCHEDULE.iter(),
491 };
492
493 BeaconSchedule(
494 ds_iter
495 .map(|dc| BeaconPoint {
496 height: dc.height,
497 beacon: Box::new(DrandBeacon::new(
498 genesis_ts,
499 self.block_delay_secs as u64,
500 dc.config,
501 )),
502 })
503 .collect(),
504 )
505 }
506
507 pub fn epoch(&self, height: Height) -> ChainEpoch {
508 self.height_infos
509 .iter()
510 .sorted_by_key(|(_, info)| info.epoch)
511 .rev()
512 .find_map(|(infos_height, info)| {
513 if *infos_height == height {
514 Some(info.epoch)
515 } else {
516 None
517 }
518 })
519 .unwrap_or(0)
520 }
521
522 pub async fn genesis_bytes<DB: SettingsStore>(
523 &self,
524 db: &DB,
525 ) -> anyhow::Result<Option<Vec<u8>>> {
526 Ok(match self.network {
527 NetworkChain::Mainnet => Some(mainnet::DEFAULT_GENESIS.to_vec()),
528 NetworkChain::Calibnet => Some(calibnet::DEFAULT_GENESIS.to_vec()),
529 NetworkChain::Butterflynet => Some(butterflynet::fetch_genesis(db).await?),
531 NetworkChain::Devnet(_) => None,
532 })
533 }
534
535 pub fn is_testnet(&self) -> bool {
536 self.network.is_testnet()
537 }
538
539 pub fn is_devnet(&self) -> bool {
540 self.network.is_devnet()
541 }
542
543 pub fn genesis_network_version(&self) -> NetworkVersion {
544 self.genesis_network
545 }
546
547 pub fn initial_fil_reserved(&self, network_version: NetworkVersion) -> &TokenAmount {
548 match &self.upgrade_teep_initial_fil_reserved {
549 Some(fil) if network_version >= NetworkVersion::V25 => fil,
550 _ => &INITIAL_FIL_RESERVED,
551 }
552 }
553
554 pub fn initial_fil_reserved_at_height(&self, height: i64) -> &TokenAmount {
555 let network_version = self.network_version(height);
556 self.initial_fil_reserved(network_version)
557 }
558}
559
560impl Default for ChainConfig {
561 fn default() -> Self {
562 ChainConfig::mainnet()
563 }
564}
565
566pub(crate) fn parse_bootstrap_peers(bootstrap_peer_list: &str) -> Vec<Multiaddr> {
567 bootstrap_peer_list
568 .split('\n')
569 .filter(|s| !s.is_empty())
570 .map(|s| {
571 Multiaddr::from_str(s).unwrap_or_else(|e| panic!("invalid bootstrap peer {s}: {e}"))
572 })
573 .collect()
574}
575
576#[allow(dead_code)]
577fn get_upgrade_epoch_by_height<'a>(
578 mut height_infos: impl Iterator<Item = &'a (Height, HeightInfo)>,
579 height: Height,
580) -> Option<ChainEpoch> {
581 height_infos.find_map(|(infos_height, info)| {
582 if *infos_height == height {
583 Some(info.epoch)
584 } else {
585 None
586 }
587 })
588}
589
590fn get_upgrade_height_from_env(env_var_key: &str) -> Option<ChainEpoch> {
591 if let Ok(value) = std::env::var(env_var_key) {
592 if let Ok(epoch) = value.parse() {
593 return Some(epoch);
594 } else {
595 warn!("Failed to parse {env_var_key}={value}, value should be an integer");
596 }
597 }
598 None
599}
600
601#[macro_export]
602macro_rules! make_height {
603 ($id:ident,$epoch:expr) => {
604 (
605 Height::$id,
606 HeightInfo {
607 epoch: $epoch,
608 bundle: None,
609 },
610 )
611 };
612 ($id:ident,$epoch:expr,$bundle:expr) => {
613 (
614 Height::$id,
615 HeightInfo {
616 epoch: $epoch,
617 bundle: Some(Cid::try_from($bundle).unwrap()),
618 },
619 )
620 };
621}
622
623pub fn calculate_expected_epoch(
630 now_timestamp: u64,
631 genesis_timestamp: u64,
632 block_delay_secs: u32,
633) -> i64 {
634 (now_timestamp.saturating_sub(genesis_timestamp) / block_delay_secs as u64) as i64
635}
636
637#[cfg(test)]
638mod tests {
639 use super::*;
640
641 fn heights_are_present(height_infos: &HashMap<Height, HeightInfo>) {
642 const REQUIRED_HEIGHTS: [Height; 30] = [
645 Height::Breeze,
646 Height::Smoke,
647 Height::Ignition,
648 Height::Refuel,
649 Height::Assembly,
650 Height::Tape,
651 Height::Liftoff,
652 Height::Kumquat,
653 Height::Calico,
654 Height::Persian,
655 Height::Orange,
656 Height::Claus,
657 Height::Trust,
658 Height::Norwegian,
659 Height::Turbo,
660 Height::Hyperdrive,
661 Height::Chocolate,
662 Height::OhSnap,
663 Height::Skyr,
664 Height::Shark,
665 Height::Hygge,
666 Height::Lightning,
667 Height::Thunder,
668 Height::Watermelon,
669 Height::Dragon,
670 Height::Phoenix,
671 Height::Waffle,
672 Height::TukTuk,
673 Height::Teep,
674 Height::GoldenWeek,
675 ];
676
677 for height in &REQUIRED_HEIGHTS {
678 assert!(height_infos.get(height).is_some());
679 }
680 }
681
682 #[test]
683 fn test_mainnet_heights() {
684 heights_are_present(&mainnet::HEIGHT_INFOS);
685 }
686
687 #[test]
688 fn test_calibnet_heights() {
689 heights_are_present(&calibnet::HEIGHT_INFOS);
690 }
691
692 #[test]
693 fn test_devnet_heights() {
694 heights_are_present(&devnet::HEIGHT_INFOS);
695 }
696
697 #[test]
698 fn test_butterflynet_heights() {
699 heights_are_present(&butterflynet::HEIGHT_INFOS);
700 }
701
702 #[test]
703 fn test_get_upgrade_height_no_env_var() {
704 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_1");
705 assert_eq!(epoch, None);
706 }
707
708 #[test]
709 fn test_get_upgrade_height_valid_env_var() {
710 unsafe { std::env::set_var("FOREST_TEST_VAR_2", "10") };
711 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_2");
712 assert_eq!(epoch, Some(10));
713 }
714
715 #[test]
716 fn test_get_upgrade_height_invalid_env_var() {
717 unsafe { std::env::set_var("FOREST_TEST_VAR_3", "foo") };
718 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_3");
719 assert_eq!(epoch, None);
720 }
721
722 #[test]
723 fn test_calculate_expected_epoch() {
724 assert_eq!(0, calculate_expected_epoch(0, 0, 1));
726 assert_eq!(5, calculate_expected_epoch(5, 0, 1));
727
728 let mainnet_genesis = 1598306400;
729 let mainnet_block_delay = 30;
730
731 assert_eq!(
732 0,
733 calculate_expected_epoch(mainnet_genesis, mainnet_genesis, mainnet_block_delay)
734 );
735
736 assert_eq!(
737 0,
738 calculate_expected_epoch(
739 mainnet_genesis + mainnet_block_delay as u64 - 1,
740 mainnet_genesis,
741 mainnet_block_delay
742 )
743 );
744
745 assert_eq!(
746 1,
747 calculate_expected_epoch(
748 mainnet_genesis + mainnet_block_delay as u64,
749 mainnet_genesis,
750 mainnet_block_delay
751 )
752 );
753 }
754
755 #[test]
756 fn network_chain_display() {
757 assert_eq!(
758 NetworkChain::Mainnet.to_string(),
759 mainnet::NETWORK_COMMON_NAME
760 );
761 assert_eq!(
762 NetworkChain::Calibnet.to_string(),
763 calibnet::NETWORK_COMMON_NAME
764 );
765 assert_eq!(
766 NetworkChain::Butterflynet.to_string(),
767 butterflynet::NETWORK_COMMON_NAME
768 );
769 assert_eq!(
770 NetworkChain::Devnet("dummydevnet".into()).to_string(),
771 "dummydevnet"
772 );
773 }
774
775 #[test]
776 fn chain_config() {
777 ChainConfig::mainnet();
778 ChainConfig::calibnet();
779 ChainConfig::devnet();
780 ChainConfig::butterflynet();
781 }
782
783 #[test]
784 fn network_version() {
785 let cfg = ChainConfig::calibnet();
786 assert_eq!(cfg.network_version(1_013_134 - 1), NetworkVersion::V20);
787 assert_eq!(cfg.network_version(1_013_134), NetworkVersion::V20);
788 assert_eq!(cfg.network_version(1_013_134 + 1), NetworkVersion::V21);
789 assert_eq!(cfg.network_version_revision(1_013_134 + 1), 0);
790 assert_eq!(cfg.network_version(1_070_494), NetworkVersion::V21);
791 assert_eq!(cfg.network_version_revision(1_070_494), 0);
792 assert_eq!(cfg.network_version(1_070_494 + 1), NetworkVersion::V21);
793 assert_eq!(cfg.network_version_revision(1_070_494 + 1), 1);
794 }
795
796 #[test]
797 fn test_network_height_with_actor_bundle() {
798 let cfg = ChainConfig::mainnet();
799 let info = cfg.network_height_with_actor_bundle(5_348_280 + 1).unwrap();
800 assert_eq!(info.height, Height::GoldenWeek);
801 let info = cfg.network_height_with_actor_bundle(5_348_280).unwrap();
802 assert_eq!(info.height, Height::Teep);
804 let info = cfg.network_height_with_actor_bundle(5_348_280 - 1).unwrap();
805 assert_eq!(info.height, Height::Teep);
806 assert!(cfg.network_height_with_actor_bundle(1).is_none());
807 assert!(cfg.network_height_with_actor_bundle(0).is_none());
808 }
809}