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