iota_proxy/
config.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use core::time::Duration;
6use std::net::SocketAddr;
7
8use anyhow::{Context, Result};
9use serde::{Deserialize, Serialize, de::DeserializeOwned};
10use serde_with::{DurationSeconds, serde_as};
11use tracing::debug;
12
13#[serde_as]
14#[derive(Clone, Debug, Deserialize, Serialize)]
15#[serde(rename_all = "kebab-case")]
16pub struct ProxyConfig {
17    pub network: String,
18    pub listen_address: SocketAddr,
19    pub remote_write: RemoteWriteConfig,
20    pub dynamic_peers: DynamicPeerValidationConfig,
21    pub static_peers: Option<StaticPeerValidationConfig>,
22    pub metrics_address: String,
23    pub histogram_address: String,
24}
25
26#[serde_as]
27#[derive(Clone, Debug, Deserialize, Serialize, Default)]
28#[serde(rename_all = "kebab-case")]
29pub struct RemoteWriteConfig {
30    // TODO upgrade to https
31    /// the remote_write url to post data to
32    pub url: String,
33    /// username is used for posting data to the remote_write api
34    pub username: String,
35    pub password: String,
36
37    /// Sets the maximum idle connection per host allowed in the pool.
38    /// <https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.pool_max_idle_per_host>
39    #[serde(default = "pool_max_idle_per_host_default")]
40    pub pool_max_idle_per_host: usize,
41}
42
43/// DynamicPeerValidationConfig controls what iota-node & iota-bridge binaries
44/// that are functioning as a validator that we'll speak with. Peer in this case
45/// is peers within the consensus committee, for each epoch.  This membership is
46/// determined dynamically for each epoch via json-rpc calls to a full node.
47#[serde_as]
48#[derive(Clone, Debug, Deserialize, Serialize)]
49#[serde(rename_all = "kebab-case")]
50pub struct DynamicPeerValidationConfig {
51    /// url is the json-rpc url we use to obtain valid peers on the blockchain
52    pub url: String,
53    #[serde_as(as = "DurationSeconds<u64>")]
54    pub interval: Duration,
55    /// if certificate_file and private_key are not provided, we'll create a
56    /// self-signed cert using this hostname
57    #[serde(default = "hostname_default")]
58    pub hostname: Option<String>,
59
60    /// incoming client connections to this proxy will be presented with this
61    /// pub key please use an absolute path
62    pub certificate_file: Option<String>,
63    /// private key for tls
64    /// please use an absolute path
65    pub private_key: Option<String>,
66}
67
68/// StaticPeerValidationConfig, unlike the DynamicPeerValidationConfig, is not
69/// determined dynamically from rpc calls.  It instead searches a local
70/// directory for pub keys that we will add to an allow list.
71#[serde_as]
72#[derive(Clone, Debug, Deserialize, Serialize)]
73#[serde(rename_all = "kebab-case")]
74pub struct StaticPeerValidationConfig {
75    pub pub_keys: Vec<StaticPubKey>,
76}
77
78/// StaticPubKey holds a human friendly name, ip and the key file for the pub
79/// key if you don't have a valid public routable ip, use an ip from
80/// 169.254.0.0/16.
81#[serde_as]
82#[derive(Clone, Debug, Deserialize, Serialize)]
83#[serde(rename_all = "kebab-case")]
84pub struct StaticPubKey {
85    /// friendly name we will see in metrics
86    pub name: String,
87    /// the peer_id from a node config file (Ed25519 PublicKey)
88    pub peer_id: String,
89}
90
91/// the default idle worker per host (reqwest to remote write url call)
92fn pool_max_idle_per_host_default() -> usize {
93    8
94}
95
96/// the default hostname we will use if not provided
97fn hostname_default() -> Option<String> {
98    Some("localhost".to_string())
99}
100
101/// load our config file from a path
102pub fn load<P: AsRef<std::path::Path>, T: DeserializeOwned + Serialize>(path: P) -> Result<T> {
103    let path = path.as_ref();
104    debug!("Reading config from {:?}", path);
105    Ok(serde_yaml::from_reader(
106        std::fs::File::open(path).context(format!("cannot open {:?}", path))?,
107    )?)
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    #[test]
114    fn config_load() {
115        const TEMPLATE: &str = include_str!("./data/config.yaml");
116
117        let _template: ProxyConfig = serde_yaml::from_str(TEMPLATE).unwrap();
118    }
119}