1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
    str::FromStr,
};

use chrono::Duration;
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DurationSeconds};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(transparent)]
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
pub struct ChunkSize(pub u32);
impl Default for ChunkSize {
    fn default() -> Self {
        ChunkSize(500_000)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(transparent)]
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
pub struct BufferSize(pub u32);
impl Default for BufferSize {
    fn default() -> Self {
        BufferSize(1)
    }
}

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default)]
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
pub struct Client {
    pub data_dir: PathBuf,
    pub genesis_file: Option<String>,
    pub enable_rpc: bool,
    pub enable_metrics_endpoint: bool,
    pub enable_health_check: bool,
    /// If this is true, then we do not validate the imported snapshot.
    /// Otherwise, we validate and compute the states.
    pub snapshot: bool,
    /// If this is true, delete the snapshot at `snapshot_path` if it's a local file.
    pub consume_snapshot: bool,
    pub snapshot_height: Option<i64>,
    pub snapshot_head: Option<i64>,
    pub snapshot_path: Option<PathBuf>,
    /// Skips loading import CAR file and assumes it's already been loaded.
    /// Will use the CIDs in the header of the file to index the chain.
    pub skip_load: bool,
    /// When importing CAR files, chunk key-value pairs before committing them
    /// to the database.
    pub chunk_size: ChunkSize,
    /// When importing CAR files, maintain a read-ahead buffer measured in
    /// number of chunks.
    pub buffer_size: BufferSize,
    pub encrypt_keystore: bool,
    /// Metrics bind, e.g. 127.0.0.1:6116
    pub metrics_address: SocketAddr,
    /// RPC bind, e.g. 127.0.0.1:1234
    pub rpc_address: SocketAddr,
    pub healthcheck_address: SocketAddr,
    /// Period of validity for JWT in seconds. Defaults to 60 days.
    #[serde_as(as = "DurationSeconds<i64>")]
    #[cfg_attr(test, arbitrary(gen(
        // Note: this a workaround for `chrono` not supporting `i64::MIN` as a valid duration.
        // [[https://github.com/chronotope/chrono/blob/dc196062650c05528cbe259e340210f0340a05d1/src/time_delta.rs#L223-L232]]
        |g| Duration::try_milliseconds(i64::arbitrary(g).max(-i64::MAX)).expect("Infallible")
    )))]
    pub token_exp: Duration,
    /// Load actors from the bundle file (possibly generating it if it doesn't exist)
    pub load_actors: bool,
    /// `TTL` to set for Ethereum `Hash` to `Cid` entries or `None` to never reclaim them.
    pub eth_mapping_ttl: Option<u32>,
}

impl Default for Client {
    fn default() -> Self {
        let dir = ProjectDirs::from("com", "ChainSafe", "Forest").expect("failed to find project directories, please set FOREST_CONFIG_PATH environment variable manually.");
        Self {
            data_dir: dir.data_dir().to_path_buf(),
            genesis_file: None,
            enable_rpc: true,
            enable_metrics_endpoint: true,
            enable_health_check: true,
            snapshot_path: None,
            snapshot: false,
            consume_snapshot: false,
            snapshot_height: None,
            snapshot_head: None,
            skip_load: false,
            chunk_size: ChunkSize::default(),
            buffer_size: BufferSize::default(),
            encrypt_keystore: true,
            metrics_address: FromStr::from_str("0.0.0.0:6116").unwrap(),
            rpc_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), crate::rpc::DEFAULT_PORT),
            healthcheck_address: SocketAddr::new(
                IpAddr::V4(Ipv4Addr::LOCALHOST),
                crate::health::DEFAULT_HEALTHCHECK_PORT,
            ),
            token_exp: Duration::try_seconds(5184000).expect("Infallible"), // 60 Days = 5184000 Seconds
            load_actors: true,
            eth_mapping_ttl: None,
        }
    }
}