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: None,
350 enable_indexer: false,
351 enable_receipt_event_caching: true,
352 default_max_fee: TokenAmount::zero(),
353 }
354 }
355
356 pub fn devnet() -> Self {
357 use devnet::*;
358 Self {
359 network: NetworkChain::Devnet("devnet".to_string()),
360 genesis_cid: None,
361 bootstrap_peers: Vec::new(),
362 block_delay_secs: env_or_default(ENV_FOREST_BLOCK_DELAY_SECS, 4),
363 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 1),
364 genesis_network: *GENESIS_NETWORK_VERSION,
365 height_infos: HEIGHT_INFOS.clone(),
366 policy: make_devnet_policy!(v13).into(),
367 eth_chain_id: ETH_CHAIN_ID,
368 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
369 fip0081_ramp_duration_epochs: env_or_default(ENV_PLEDGE_RULE_RAMP, 200),
371 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_400_000_000)),
373 f3_enabled: false,
374 f3_consensus: false,
375 f3_bootstrap_epoch: -1,
376 f3_initial_power_table: None,
377 enable_indexer: false,
378 enable_receipt_event_caching: true,
379 default_max_fee: TokenAmount::zero(),
380 }
381 }
382
383 pub fn butterflynet() -> Self {
384 use butterflynet::*;
385 Self {
386 network: NetworkChain::Butterflynet,
387 genesis_cid: Some(GENESIS_CID.to_string()),
388 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
389 block_delay_secs: env_or_default(
390 ENV_FOREST_BLOCK_DELAY_SECS,
391 EPOCH_DURATION_SECONDS as u32,
392 ),
393 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 6),
394 genesis_network: GENESIS_NETWORK_VERSION,
395 height_infos: HEIGHT_INFOS.clone(),
396 policy: make_butterfly_policy!(v13).into(),
397 eth_chain_id: ETH_CHAIN_ID,
398 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
399 fip0081_ramp_duration_epochs: env_or_default(
401 ENV_PLEDGE_RULE_RAMP,
402 365 * EPOCHS_IN_DAY as u64,
403 ),
404 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_600_000_000)),
406 f3_enabled: true,
407 f3_consensus: true,
408 f3_bootstrap_epoch: 1000,
409 f3_initial_power_table: None,
410 enable_indexer: false,
411 enable_receipt_event_caching: true,
412 default_max_fee: TokenAmount::zero(),
413 }
414 }
415
416 pub fn from_chain(network_chain: &NetworkChain) -> Self {
417 match network_chain {
418 NetworkChain::Mainnet => Self::mainnet(),
419 NetworkChain::Calibnet => Self::calibnet(),
420 NetworkChain::Butterflynet => Self::butterflynet(),
421 NetworkChain::Devnet(name) => Self {
422 network: NetworkChain::Devnet(name.clone()),
423 ..Self::devnet()
424 },
425 }
426 }
427
428 fn network_height(&self, epoch: ChainEpoch) -> Option<Height> {
429 self.height_infos
430 .iter()
431 .sorted_by_key(|(_, info)| info.epoch)
432 .rev()
433 .find(|(_, info)| epoch > info.epoch)
434 .map(|(height, _)| *height)
435 }
436
437 pub fn network_height_with_actor_bundle<'a>(
439 &'a self,
440 epoch: ChainEpoch,
441 ) -> Option<HeightInfoWithActorManifest<'a>> {
442 if let Some((height, info, manifest_cid)) = self
443 .height_infos
444 .iter()
445 .sorted_by_key(|(_, info)| info.epoch)
446 .rev()
447 .filter_map(|(height, info)| info.bundle.map(|bundle| (*height, info, bundle)))
448 .find(|(_, info, _)| epoch > info.epoch)
449 {
450 Some(HeightInfoWithActorManifest {
451 height,
452 info,
453 manifest_cid,
454 })
455 } else {
456 None
457 }
458 }
459
460 pub fn network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
463 self.network_height(epoch)
464 .map(NetworkVersion::from)
465 .unwrap_or(self.genesis_network_version())
466 .max(self.genesis_network)
467 }
468
469 pub fn network_version_revision(&self, epoch: ChainEpoch) -> i64 {
472 if let Some(height) = self.network_height(epoch) {
473 let nv = NetworkVersion::from(height);
474 if let Some(rev0_height) = Height::iter().find(|h| NetworkVersion::from(*h) == nv) {
475 return (height as i64) - (rev0_height as i64);
476 }
477 }
478 0
479 }
480
481 pub fn get_beacon_schedule(&self, genesis_ts: u64) -> BeaconSchedule {
482 let ds_iter = match self.network {
483 NetworkChain::Mainnet => mainnet::DRAND_SCHEDULE.iter(),
484 NetworkChain::Calibnet => calibnet::DRAND_SCHEDULE.iter(),
485 NetworkChain::Butterflynet => butterflynet::DRAND_SCHEDULE.iter(),
486 NetworkChain::Devnet(_) => devnet::DRAND_SCHEDULE.iter(),
487 };
488
489 BeaconSchedule(
490 ds_iter
491 .map(|dc| BeaconPoint {
492 height: dc.height,
493 beacon: Box::new(DrandBeacon::new(
494 genesis_ts,
495 self.block_delay_secs as u64,
496 dc.config,
497 )),
498 })
499 .collect(),
500 )
501 }
502
503 pub fn epoch(&self, height: Height) -> ChainEpoch {
504 self.height_infos
505 .iter()
506 .sorted_by_key(|(_, info)| info.epoch)
507 .rev()
508 .find_map(|(infos_height, info)| {
509 if *infos_height == height {
510 Some(info.epoch)
511 } else {
512 None
513 }
514 })
515 .unwrap_or(0)
516 }
517
518 pub async fn genesis_bytes<DB: SettingsStore>(
519 &self,
520 db: &DB,
521 ) -> anyhow::Result<Option<Vec<u8>>> {
522 Ok(match self.network {
523 NetworkChain::Mainnet => Some(mainnet::DEFAULT_GENESIS.to_vec()),
524 NetworkChain::Calibnet => Some(calibnet::DEFAULT_GENESIS.to_vec()),
525 NetworkChain::Butterflynet => Some(butterflynet::fetch_genesis(db).await?),
527 NetworkChain::Devnet(_) => None,
528 })
529 }
530
531 pub fn is_testnet(&self) -> bool {
532 self.network.is_testnet()
533 }
534
535 pub fn is_devnet(&self) -> bool {
536 self.network.is_devnet()
537 }
538
539 pub fn genesis_network_version(&self) -> NetworkVersion {
540 self.genesis_network
541 }
542
543 pub fn initial_fil_reserved(&self, network_version: NetworkVersion) -> &TokenAmount {
544 match &self.upgrade_teep_initial_fil_reserved {
545 Some(fil) if network_version >= NetworkVersion::V25 => fil,
546 _ => &INITIAL_FIL_RESERVED,
547 }
548 }
549
550 pub fn initial_fil_reserved_at_height(&self, height: i64) -> &TokenAmount {
551 let network_version = self.network_version(height);
552 self.initial_fil_reserved(network_version)
553 }
554}
555
556impl Default for ChainConfig {
557 fn default() -> Self {
558 ChainConfig::mainnet()
559 }
560}
561
562pub(crate) fn parse_bootstrap_peers(bootstrap_peer_list: &str) -> Vec<Multiaddr> {
563 bootstrap_peer_list
564 .split('\n')
565 .filter(|s| !s.is_empty())
566 .map(|s| {
567 Multiaddr::from_str(s).unwrap_or_else(|e| panic!("invalid bootstrap peer {s}: {e}"))
568 })
569 .collect()
570}
571
572#[allow(dead_code)]
573fn get_upgrade_epoch_by_height<'a>(
574 mut height_infos: impl Iterator<Item = &'a (Height, HeightInfo)>,
575 height: Height,
576) -> Option<ChainEpoch> {
577 height_infos.find_map(|(infos_height, info)| {
578 if *infos_height == height {
579 Some(info.epoch)
580 } else {
581 None
582 }
583 })
584}
585
586fn get_upgrade_height_from_env(env_var_key: &str) -> Option<ChainEpoch> {
587 if let Ok(value) = std::env::var(env_var_key) {
588 if let Ok(epoch) = value.parse() {
589 return Some(epoch);
590 } else {
591 warn!("Failed to parse {env_var_key}={value}, value should be an integer");
592 }
593 }
594 None
595}
596
597#[macro_export]
598macro_rules! make_height {
599 ($id:ident,$epoch:expr) => {
600 (
601 Height::$id,
602 HeightInfo {
603 epoch: $epoch,
604 bundle: None,
605 },
606 )
607 };
608 ($id:ident,$epoch:expr,$bundle:expr) => {
609 (
610 Height::$id,
611 HeightInfo {
612 epoch: $epoch,
613 bundle: Some(Cid::try_from($bundle).unwrap()),
614 },
615 )
616 };
617}
618
619pub fn calculate_expected_epoch(
626 now_timestamp: u64,
627 genesis_timestamp: u64,
628 block_delay_secs: u32,
629) -> i64 {
630 (now_timestamp.saturating_sub(genesis_timestamp) / block_delay_secs as u64) as i64
631}
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636
637 fn heights_are_present(height_infos: &HashMap<Height, HeightInfo>) {
638 const REQUIRED_HEIGHTS: [Height; 30] = [
641 Height::Breeze,
642 Height::Smoke,
643 Height::Ignition,
644 Height::Refuel,
645 Height::Assembly,
646 Height::Tape,
647 Height::Liftoff,
648 Height::Kumquat,
649 Height::Calico,
650 Height::Persian,
651 Height::Orange,
652 Height::Claus,
653 Height::Trust,
654 Height::Norwegian,
655 Height::Turbo,
656 Height::Hyperdrive,
657 Height::Chocolate,
658 Height::OhSnap,
659 Height::Skyr,
660 Height::Shark,
661 Height::Hygge,
662 Height::Lightning,
663 Height::Thunder,
664 Height::Watermelon,
665 Height::Dragon,
666 Height::Phoenix,
667 Height::Waffle,
668 Height::TukTuk,
669 Height::Teep,
670 Height::GoldenWeek,
671 ];
672
673 for height in &REQUIRED_HEIGHTS {
674 assert!(height_infos.get(height).is_some());
675 }
676 }
677
678 #[test]
679 fn test_mainnet_heights() {
680 heights_are_present(&mainnet::HEIGHT_INFOS);
681 }
682
683 #[test]
684 fn test_calibnet_heights() {
685 heights_are_present(&calibnet::HEIGHT_INFOS);
686 }
687
688 #[test]
689 fn test_devnet_heights() {
690 heights_are_present(&devnet::HEIGHT_INFOS);
691 }
692
693 #[test]
694 fn test_butterflynet_heights() {
695 heights_are_present(&butterflynet::HEIGHT_INFOS);
696 }
697
698 #[test]
699 fn test_get_upgrade_height_no_env_var() {
700 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_1");
701 assert_eq!(epoch, None);
702 }
703
704 #[test]
705 fn test_get_upgrade_height_valid_env_var() {
706 unsafe { std::env::set_var("FOREST_TEST_VAR_2", "10") };
707 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_2");
708 assert_eq!(epoch, Some(10));
709 }
710
711 #[test]
712 fn test_get_upgrade_height_invalid_env_var() {
713 unsafe { std::env::set_var("FOREST_TEST_VAR_3", "foo") };
714 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_3");
715 assert_eq!(epoch, None);
716 }
717
718 #[test]
719 fn test_calculate_expected_epoch() {
720 assert_eq!(0, calculate_expected_epoch(0, 0, 1));
722 assert_eq!(5, calculate_expected_epoch(5, 0, 1));
723
724 let mainnet_genesis = 1598306400;
725 let mainnet_block_delay = 30;
726
727 assert_eq!(
728 0,
729 calculate_expected_epoch(mainnet_genesis, mainnet_genesis, mainnet_block_delay)
730 );
731
732 assert_eq!(
733 0,
734 calculate_expected_epoch(
735 mainnet_genesis + mainnet_block_delay as u64 - 1,
736 mainnet_genesis,
737 mainnet_block_delay
738 )
739 );
740
741 assert_eq!(
742 1,
743 calculate_expected_epoch(
744 mainnet_genesis + mainnet_block_delay as u64,
745 mainnet_genesis,
746 mainnet_block_delay
747 )
748 );
749 }
750
751 #[test]
752 fn network_chain_display() {
753 assert_eq!(
754 NetworkChain::Mainnet.to_string(),
755 mainnet::NETWORK_COMMON_NAME
756 );
757 assert_eq!(
758 NetworkChain::Calibnet.to_string(),
759 calibnet::NETWORK_COMMON_NAME
760 );
761 assert_eq!(
762 NetworkChain::Butterflynet.to_string(),
763 butterflynet::NETWORK_COMMON_NAME
764 );
765 assert_eq!(
766 NetworkChain::Devnet("dummydevnet".into()).to_string(),
767 "dummydevnet"
768 );
769 }
770
771 #[test]
772 fn chain_config() {
773 ChainConfig::mainnet();
774 ChainConfig::calibnet();
775 ChainConfig::devnet();
776 ChainConfig::butterflynet();
777 }
778
779 #[test]
780 fn network_version() {
781 let cfg = ChainConfig::calibnet();
782 assert_eq!(cfg.network_version(1_013_134 - 1), NetworkVersion::V20);
783 assert_eq!(cfg.network_version(1_013_134), NetworkVersion::V20);
784 assert_eq!(cfg.network_version(1_013_134 + 1), NetworkVersion::V21);
785 assert_eq!(cfg.network_version_revision(1_013_134 + 1), 0);
786 assert_eq!(cfg.network_version(1_070_494), NetworkVersion::V21);
787 assert_eq!(cfg.network_version_revision(1_070_494), 0);
788 assert_eq!(cfg.network_version(1_070_494 + 1), NetworkVersion::V21);
789 assert_eq!(cfg.network_version_revision(1_070_494 + 1), 1);
790 }
791
792 #[test]
793 fn test_network_height_with_actor_bundle() {
794 let cfg = ChainConfig::mainnet();
795 let info = cfg.network_height_with_actor_bundle(5_348_280 + 1).unwrap();
796 assert_eq!(info.height, Height::GoldenWeek);
797 let info = cfg.network_height_with_actor_bundle(5_348_280).unwrap();
798 assert_eq!(info.height, Height::Teep);
800 let info = cfg.network_height_with_actor_bundle(5_348_280 - 1).unwrap();
801 assert_eq!(info.height, Height::Teep);
802 assert!(cfg.network_height_with_actor_bundle(1).is_none());
803 assert!(cfg.network_height_with_actor_bundle(0).is_none());
804 }
805}