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::{Display, EnumIter, IntoEnumIterator};
15use tracing::warn;
16
17use crate::beacon::{BeaconPoint, BeaconSchedule, DrandBeacon, DrandConfig};
18use crate::db::SettingsStore;
19use crate::eth::EthChainId;
20use crate::shim::{
21 clock::{ChainEpoch, EPOCH_DURATION_SECONDS, EPOCHS_IN_DAY},
22 econ::TokenAmount,
23 machine::BuiltinActorManifest,
24 runtime::Policy,
25 sector::{RegisteredPoStProofV3, RegisteredSealProofV3},
26 version::NetworkVersion,
27};
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::V27;
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, derive_more::Display,
64)]
65#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
66#[serde(tag = "type", content = "name", rename_all = "lowercase")]
67#[display(rename_all = "lowercase")]
68pub enum NetworkChain {
69 #[default]
70 Mainnet,
71 Calibnet,
72 Butterflynet,
73 Devnet(String),
74}
75
76impl FromStr for NetworkChain {
77 type Err = anyhow::Error;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 match s {
81 mainnet::NETWORK_COMMON_NAME | mainnet::NETWORK_GENESIS_NAME => {
82 Ok(NetworkChain::Mainnet)
83 }
84 calibnet::NETWORK_COMMON_NAME | calibnet::NETWORK_GENESIS_NAME => {
85 Ok(NetworkChain::Calibnet)
86 }
87 butterflynet::NETWORK_COMMON_NAME => Ok(NetworkChain::Butterflynet),
88 name => Ok(NetworkChain::Devnet(name.to_owned())),
89 }
90 }
91}
92
93impl NetworkChain {
94 pub fn genesis_name(&self) -> GenesisNetworkName {
100 match self {
101 NetworkChain::Mainnet => mainnet::NETWORK_GENESIS_NAME.into(),
102 NetworkChain::Calibnet => calibnet::NETWORK_GENESIS_NAME.into(),
103 _ => self.to_string().into(),
104 }
105 }
106 pub fn from_genesis(cid: &Cid) -> Option<Self> {
109 if cid == &*mainnet::GENESIS_CID {
110 Some(Self::Mainnet)
111 } else if cid == &*calibnet::GENESIS_CID {
112 Some(Self::Calibnet)
113 } else if cid == &*butterflynet::GENESIS_CID {
114 Some(Self::Butterflynet)
115 } else {
116 None
117 }
118 }
119
120 pub fn from_genesis_or_devnet_placeholder(cid: &Cid) -> Self {
125 Self::from_genesis(cid).unwrap_or(Self::Devnet(String::from("devnet")))
126 }
127
128 pub fn is_testnet(&self) -> bool {
129 !matches!(self, NetworkChain::Mainnet)
130 }
131
132 pub fn is_devnet(&self) -> bool {
133 matches!(self, NetworkChain::Devnet(..))
134 }
135}
136
137#[derive(
139 Debug, Default, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, EnumIter,
140)]
141#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
142pub enum Height {
143 #[default]
144 Breeze,
145 Smoke,
146 Ignition,
147 Refuel,
148 Assembly,
149 Tape,
150 Liftoff,
151 Kumquat,
152 Calico,
153 Persian,
154 Orange,
155 Claus,
156 Trust,
157 Norwegian,
158 Turbo,
159 Hyperdrive,
160 Chocolate,
161 OhSnap,
162 Skyr,
163 Shark,
164 Hygge,
165 Lightning,
166 Thunder,
167 Watermelon,
168 WatermelonFix,
169 WatermelonFix2,
170 Dragon,
171 DragonFix,
172 Phoenix,
173 Waffle,
174 TukTuk,
175 Teep,
176 Tock,
177 TockFix,
178 GoldenWeek,
179 Xxx,
180}
181
182impl From<Height> for NetworkVersion {
183 fn from(height: Height) -> NetworkVersion {
184 match height {
185 Height::Breeze => NetworkVersion::V1,
186 Height::Smoke => NetworkVersion::V2,
187 Height::Ignition => NetworkVersion::V3,
188 Height::Refuel => NetworkVersion::V3,
189 Height::Assembly => NetworkVersion::V4,
190 Height::Tape => NetworkVersion::V5,
191 Height::Liftoff => NetworkVersion::V5,
192 Height::Kumquat => NetworkVersion::V6,
193 Height::Calico => NetworkVersion::V7,
194 Height::Persian => NetworkVersion::V8,
195 Height::Orange => NetworkVersion::V9,
196 Height::Claus => NetworkVersion::V9,
197 Height::Trust => NetworkVersion::V10,
198 Height::Norwegian => NetworkVersion::V11,
199 Height::Turbo => NetworkVersion::V12,
200 Height::Hyperdrive => NetworkVersion::V13,
201 Height::Chocolate => NetworkVersion::V14,
202 Height::OhSnap => NetworkVersion::V15,
203 Height::Skyr => NetworkVersion::V16,
204 Height::Shark => NetworkVersion::V17,
205 Height::Hygge => NetworkVersion::V18,
206 Height::Lightning => NetworkVersion::V19,
207 Height::Thunder => NetworkVersion::V20,
208 Height::Watermelon => NetworkVersion::V21,
209 Height::WatermelonFix => NetworkVersion::V21,
210 Height::WatermelonFix2 => NetworkVersion::V21,
211 Height::Dragon => NetworkVersion::V22,
212 Height::DragonFix => NetworkVersion::V22,
213 Height::Phoenix => NetworkVersion::V22,
214 Height::Waffle => NetworkVersion::V23,
215 Height::TukTuk => NetworkVersion::V24,
216 Height::Teep => NetworkVersion::V25,
217 Height::Tock => NetworkVersion::V26,
218 Height::TockFix => NetworkVersion::V26,
219 Height::GoldenWeek => NetworkVersion::V27,
220 Height::Xxx => NetworkVersion::V28,
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; 31] = [
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 Height::Xxx,
676 ];
677
678 for height in &REQUIRED_HEIGHTS {
679 assert!(height_infos.get(height).is_some());
680 }
681 }
682
683 #[test]
684 fn test_mainnet_heights() {
685 heights_are_present(&mainnet::HEIGHT_INFOS);
686 }
687
688 #[test]
689 fn test_calibnet_heights() {
690 heights_are_present(&calibnet::HEIGHT_INFOS);
691 }
692
693 #[test]
694 fn test_devnet_heights() {
695 heights_are_present(&devnet::HEIGHT_INFOS);
696 }
697
698 #[test]
699 fn test_butterflynet_heights() {
700 heights_are_present(&butterflynet::HEIGHT_INFOS);
701 }
702
703 #[test]
704 fn test_get_upgrade_height_no_env_var() {
705 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_1");
706 assert_eq!(epoch, None);
707 }
708
709 #[test]
710 fn test_get_upgrade_height_valid_env_var() {
711 unsafe { std::env::set_var("FOREST_TEST_VAR_2", "10") };
712 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_2");
713 assert_eq!(epoch, Some(10));
714 }
715
716 #[test]
717 fn test_get_upgrade_height_invalid_env_var() {
718 unsafe { std::env::set_var("FOREST_TEST_VAR_3", "foo") };
719 let epoch = get_upgrade_height_from_env("FOREST_TEST_VAR_3");
720 assert_eq!(epoch, None);
721 }
722
723 #[test]
724 fn test_calculate_expected_epoch() {
725 assert_eq!(0, calculate_expected_epoch(0, 0, 1));
727 assert_eq!(5, calculate_expected_epoch(5, 0, 1));
728
729 let mainnet_genesis = 1598306400;
730 let mainnet_block_delay = 30;
731
732 assert_eq!(
733 0,
734 calculate_expected_epoch(mainnet_genesis, mainnet_genesis, mainnet_block_delay)
735 );
736
737 assert_eq!(
738 0,
739 calculate_expected_epoch(
740 mainnet_genesis + mainnet_block_delay as u64 - 1,
741 mainnet_genesis,
742 mainnet_block_delay
743 )
744 );
745
746 assert_eq!(
747 1,
748 calculate_expected_epoch(
749 mainnet_genesis + mainnet_block_delay as u64,
750 mainnet_genesis,
751 mainnet_block_delay
752 )
753 );
754 }
755
756 #[test]
757 fn network_chain_display() {
758 assert_eq!(
759 NetworkChain::Mainnet.to_string(),
760 mainnet::NETWORK_COMMON_NAME
761 );
762 assert_eq!(
763 NetworkChain::Calibnet.to_string(),
764 calibnet::NETWORK_COMMON_NAME
765 );
766 assert_eq!(
767 NetworkChain::Butterflynet.to_string(),
768 butterflynet::NETWORK_COMMON_NAME
769 );
770 assert_eq!(
771 NetworkChain::Devnet("dummydevnet".into()).to_string(),
772 "dummydevnet"
773 );
774 }
775
776 #[test]
777 fn chain_config() {
778 ChainConfig::mainnet();
779 ChainConfig::calibnet();
780 ChainConfig::devnet();
781 ChainConfig::butterflynet();
782 }
783
784 #[test]
785 fn network_version() {
786 let cfg = ChainConfig::calibnet();
787 assert_eq!(cfg.network_version(1_013_134 - 1), NetworkVersion::V20);
788 assert_eq!(cfg.network_version(1_013_134), NetworkVersion::V20);
789 assert_eq!(cfg.network_version(1_013_134 + 1), NetworkVersion::V21);
790 assert_eq!(cfg.network_version_revision(1_013_134 + 1), 0);
791 assert_eq!(cfg.network_version(1_070_494), NetworkVersion::V21);
792 assert_eq!(cfg.network_version_revision(1_070_494), 0);
793 assert_eq!(cfg.network_version(1_070_494 + 1), NetworkVersion::V21);
794 assert_eq!(cfg.network_version_revision(1_070_494 + 1), 1);
795 }
796
797 #[test]
798 fn test_network_height_with_actor_bundle() {
799 let cfg = ChainConfig::mainnet();
800 let info = cfg.network_height_with_actor_bundle(5_348_280 + 1).unwrap();
801 assert_eq!(info.height, Height::GoldenWeek);
802 let info = cfg.network_height_with_actor_bundle(5_348_280).unwrap();
803 assert_eq!(info.height, Height::Teep);
805 let info = cfg.network_height_with_actor_bundle(5_348_280 - 1).unwrap();
806 assert_eq!(info.height, Height::Teep);
807 assert!(cfg.network_height_with_actor_bundle(1).is_none());
808 assert!(cfg.network_height_with_actor_bundle(0).is_none());
809 }
810}