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 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::clock::{ChainEpoch, EPOCH_DURATION_SECONDS, EPOCHS_IN_DAY};
23use crate::shim::econ::TokenAmount;
24use crate::shim::machine::BuiltinActorManifest;
25use crate::shim::sector::{RegisteredPoStProofV3, RegisteredSealProofV3};
26use crate::shim::version::NetworkVersion;
27use crate::utils::misc::env::env_or_default;
28use crate::{make_butterfly_policy, make_calibnet_policy, make_devnet_policy, make_mainnet_policy};
29
30pub use network_name::{GenesisNetworkName, StateNetworkName};
31
32mod actors_bundle;
33pub use actors_bundle::{
34 ACTOR_BUNDLES, ACTOR_BUNDLES_METADATA, ActorBundleInfo, ActorBundleMetadata,
35 generate_actor_bundle, get_actor_bundles_metadata,
36};
37
38mod drand;
39
40pub mod network_name;
41
42pub mod butterflynet;
43pub mod calibnet;
44pub mod devnet;
45pub mod mainnet;
46
47pub mod metrics;
48
49pub const NEWEST_NETWORK_VERSION: NetworkVersion = NetworkVersion::V25;
51
52const ENV_FOREST_BLOCK_DELAY_SECS: &str = "FOREST_BLOCK_DELAY_SECS";
53const ENV_FOREST_PROPAGATION_DELAY_SECS: &str = "FOREST_PROPAGATION_DELAY_SECS";
54const ENV_PLEDGE_RULE_RAMP: &str = "FOREST_PLEDGE_RULE_RAMP";
55
56static INITIAL_FIL_RESERVED: LazyLock<TokenAmount> =
57 LazyLock::new(|| TokenAmount::from_whole(300_000_000));
58
59#[derive(
62 Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Hash, displaydoc::Display,
63)]
64#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
65#[serde(tag = "type", content = "name", rename_all = "lowercase")]
66pub enum NetworkChain {
67 #[default]
69 Mainnet,
70 Calibnet,
72 Butterflynet,
74 #[displaydoc("{0}")]
76 Devnet(String),
77}
78
79impl FromStr for NetworkChain {
80 type Err = anyhow::Error;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 match s {
84 mainnet::NETWORK_COMMON_NAME | mainnet::NETWORK_GENESIS_NAME => {
85 Ok(NetworkChain::Mainnet)
86 }
87 calibnet::NETWORK_COMMON_NAME | calibnet::NETWORK_GENESIS_NAME => {
88 Ok(NetworkChain::Calibnet)
89 }
90 butterflynet::NETWORK_COMMON_NAME => Ok(NetworkChain::Butterflynet),
91 name => Ok(NetworkChain::Devnet(name.to_owned())),
92 }
93 }
94}
95
96impl NetworkChain {
97 pub fn genesis_name(&self) -> GenesisNetworkName {
103 match self {
104 NetworkChain::Mainnet => mainnet::NETWORK_GENESIS_NAME.into(),
105 NetworkChain::Calibnet => calibnet::NETWORK_GENESIS_NAME.into(),
106 _ => self.to_string().into(),
107 }
108 }
109 pub fn from_genesis(cid: &Cid) -> Option<Self> {
112 if cid == &*mainnet::GENESIS_CID {
113 Some(Self::Mainnet)
114 } else if cid == &*calibnet::GENESIS_CID {
115 Some(Self::Calibnet)
116 } else if cid == &*butterflynet::GENESIS_CID {
117 Some(Self::Butterflynet)
118 } else {
119 None
120 }
121 }
122
123 pub fn from_genesis_or_devnet_placeholder(cid: &Cid) -> Self {
128 Self::from_genesis(cid).unwrap_or(Self::Devnet(String::from("devnet")))
129 }
130
131 pub fn is_testnet(&self) -> bool {
132 !matches!(self, NetworkChain::Mainnet)
133 }
134
135 pub fn is_devnet(&self) -> bool {
136 matches!(self, NetworkChain::Devnet(..))
137 }
138}
139
140#[derive(
142 Debug, Default, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, EnumIter,
143)]
144#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
145pub enum Height {
146 #[default]
147 Breeze,
148 Smoke,
149 Ignition,
150 Refuel,
151 Assembly,
152 Tape,
153 Liftoff,
154 Kumquat,
155 Calico,
156 Persian,
157 Orange,
158 Claus,
159 Trust,
160 Norwegian,
161 Turbo,
162 Hyperdrive,
163 Chocolate,
164 OhSnap,
165 Skyr,
166 Shark,
167 Hygge,
168 Lightning,
169 Thunder,
170 Watermelon,
171 WatermelonFix,
172 WatermelonFix2,
173 Dragon,
174 DragonFix,
175 Phoenix,
176 Waffle,
177 TukTuk,
178 Teep,
179 Tock,
180 TockFix,
181 GoldenWeek,
182}
183
184impl From<Height> for NetworkVersion {
185 fn from(height: Height) -> NetworkVersion {
186 match height {
187 Height::Breeze => NetworkVersion::V1,
188 Height::Smoke => NetworkVersion::V2,
189 Height::Ignition => NetworkVersion::V3,
190 Height::Refuel => NetworkVersion::V3,
191 Height::Assembly => NetworkVersion::V4,
192 Height::Tape => NetworkVersion::V5,
193 Height::Liftoff => NetworkVersion::V5,
194 Height::Kumquat => NetworkVersion::V6,
195 Height::Calico => NetworkVersion::V7,
196 Height::Persian => NetworkVersion::V8,
197 Height::Orange => NetworkVersion::V9,
198 Height::Claus => NetworkVersion::V9,
199 Height::Trust => NetworkVersion::V10,
200 Height::Norwegian => NetworkVersion::V11,
201 Height::Turbo => NetworkVersion::V12,
202 Height::Hyperdrive => NetworkVersion::V13,
203 Height::Chocolate => NetworkVersion::V14,
204 Height::OhSnap => NetworkVersion::V15,
205 Height::Skyr => NetworkVersion::V16,
206 Height::Shark => NetworkVersion::V17,
207 Height::Hygge => NetworkVersion::V18,
208 Height::Lightning => NetworkVersion::V19,
209 Height::Thunder => NetworkVersion::V20,
210 Height::Watermelon => NetworkVersion::V21,
211 Height::WatermelonFix => NetworkVersion::V21,
212 Height::WatermelonFix2 => NetworkVersion::V21,
213 Height::Dragon => NetworkVersion::V22,
214 Height::DragonFix => NetworkVersion::V22,
215 Height::Phoenix => NetworkVersion::V22,
216 Height::Waffle => NetworkVersion::V23,
217 Height::TukTuk => NetworkVersion::V24,
218 Height::Teep => NetworkVersion::V25,
219 Height::Tock => NetworkVersion::V26,
220 Height::TockFix => NetworkVersion::V26,
221 Height::GoldenWeek => NetworkVersion::V27,
222 }
223 }
224}
225
226#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
227#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
228pub struct HeightInfo {
229 pub epoch: ChainEpoch,
230 pub bundle: Option<Cid>,
231}
232
233pub struct HeightInfoWithActorManifest<'a> {
234 #[allow(dead_code)]
235 pub height: Height,
236 pub info: &'a HeightInfo,
237 pub manifest_cid: Cid,
238}
239
240impl<'a> HeightInfoWithActorManifest<'a> {
241 pub fn manifest(&self, store: &impl Blockstore) -> anyhow::Result<BuiltinActorManifest> {
242 BuiltinActorManifest::load_manifest(store, &self.manifest_cid)
243 }
244}
245
246#[derive(Clone)]
247struct DrandPoint<'a> {
248 pub height: ChainEpoch,
249 pub config: &'a LazyLock<DrandConfig<'a>>,
250}
251
252#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
254#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
255#[serde(default)]
256pub struct ChainConfig {
257 pub network: NetworkChain,
258 pub genesis_cid: Option<String>,
259 #[cfg_attr(test, arbitrary(gen(
260 |g: &mut quickcheck::Gen| {
261 let addr = std::net::Ipv4Addr::arbitrary(&mut *g);
262 let n = u8::arbitrary(g) as usize;
263 vec![addr.into(); n]
264 }
265 )))]
266 pub bootstrap_peers: Vec<Multiaddr>,
267 pub block_delay_secs: u32,
268 pub propagation_delay_secs: u32,
269 pub genesis_network: NetworkVersion,
270 pub height_infos: HashMap<Height, HeightInfo>,
271 #[cfg_attr(test, arbitrary(gen(|_g| Policy::default())))]
272 pub policy: Policy,
273 pub eth_chain_id: EthChainId,
274 pub breeze_gas_tamping_duration: i64,
275 pub fip0081_ramp_duration_epochs: u64,
277 pub upgrade_teep_initial_fil_reserved: Option<TokenAmount>,
279 pub f3_enabled: bool,
280 pub f3_consensus: bool,
282 pub f3_bootstrap_epoch: i64,
283 pub f3_initial_power_table: Option<Cid>,
284 pub enable_indexer: bool,
285 pub enable_receipt_event_caching: bool,
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),
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 }
320 }
321
322 pub fn calibnet() -> Self {
323 use calibnet::*;
324 Self {
325 network: NetworkChain::Calibnet,
326 genesis_cid: Some(GENESIS_CID.to_string()),
327 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
328 block_delay_secs: env_or_default(
329 ENV_FOREST_BLOCK_DELAY_SECS,
330 EPOCH_DURATION_SECONDS as u32,
331 ),
332 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 10),
333 genesis_network: GENESIS_NETWORK_VERSION,
334 height_infos: HEIGHT_INFOS.clone(),
335 policy: make_calibnet_policy!(v13),
336 eth_chain_id: ETH_CHAIN_ID,
337 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
338 fip0081_ramp_duration_epochs: 3 * EPOCHS_IN_DAY as u64,
340 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_200_000_000)),
342 f3_enabled: true,
345 f3_consensus: true,
346 f3_bootstrap_epoch: 2_081_674,
348 f3_initial_power_table: Some(
349 "bafy2bzaceab236vmmb3n4q4tkvua2n4dphcbzzxerxuey3mot4g3cov5j3r2c"
350 .parse()
351 .expect("invalid f3_initial_power_table"),
352 ),
353 enable_indexer: false,
354 enable_receipt_event_caching: true,
355 }
356 }
357
358 pub fn devnet() -> Self {
359 use devnet::*;
360 Self {
361 network: NetworkChain::Devnet("devnet".to_string()),
362 genesis_cid: None,
363 bootstrap_peers: Vec::new(),
364 block_delay_secs: env_or_default(ENV_FOREST_BLOCK_DELAY_SECS, 4),
365 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 1),
366 genesis_network: *GENESIS_NETWORK_VERSION,
367 height_infos: HEIGHT_INFOS.clone(),
368 policy: make_devnet_policy!(v13),
369 eth_chain_id: ETH_CHAIN_ID,
370 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
371 fip0081_ramp_duration_epochs: env_or_default(ENV_PLEDGE_RULE_RAMP, 200),
373 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_400_000_000)),
375 f3_enabled: false,
376 f3_consensus: false,
377 f3_bootstrap_epoch: -1,
378 f3_initial_power_table: None,
379 enable_indexer: false,
380 enable_receipt_event_caching: true,
381 }
382 }
383
384 pub fn butterflynet() -> Self {
385 use butterflynet::*;
386 Self {
387 network: NetworkChain::Butterflynet,
388 genesis_cid: Some(GENESIS_CID.to_string()),
389 bootstrap_peers: DEFAULT_BOOTSTRAP.clone(),
390 block_delay_secs: env_or_default(
391 ENV_FOREST_BLOCK_DELAY_SECS,
392 EPOCH_DURATION_SECONDS as u32,
393 ),
394 propagation_delay_secs: env_or_default(ENV_FOREST_PROPAGATION_DELAY_SECS, 6),
395 genesis_network: GENESIS_NETWORK_VERSION,
396 height_infos: HEIGHT_INFOS.clone(),
397 policy: make_butterfly_policy!(v13),
398 eth_chain_id: ETH_CHAIN_ID,
399 breeze_gas_tamping_duration: BREEZE_GAS_TAMPING_DURATION,
400 fip0081_ramp_duration_epochs: env_or_default(
402 ENV_PLEDGE_RULE_RAMP,
403 365 * EPOCHS_IN_DAY as u64,
404 ),
405 upgrade_teep_initial_fil_reserved: Some(TokenAmount::from_whole(1_600_000_000)),
407 f3_enabled: true,
408 f3_consensus: true,
409 f3_bootstrap_epoch: 1000,
410 f3_initial_power_table: None,
411 enable_indexer: false,
412 enable_receipt_event_caching: true,
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}