sui_proxy/
config.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3use anyhow::{Context, Result};
4use core::time::Duration;
5use serde::{Deserialize, Serialize, de::DeserializeOwned};
6use serde_with::{DurationSeconds, serde_as};
7use std::net::SocketAddr;
8use tracing::debug;
9
10#[serde_as]
11#[derive(Clone, Debug, Deserialize, Serialize)]
12#[serde(rename_all = "kebab-case")]
13pub struct ProxyConfig {
14    pub network: String,
15    pub listen_address: SocketAddr,
16    pub remote_write: RemoteWriteConfig,
17    pub dynamic_peers: DynamicPeerValidationConfig,
18    pub static_peers: Option<StaticPeerValidationConfig>,
19    pub metrics_address: String,
20    pub histogram_address: String,
21}
22
23#[serde_as]
24#[derive(Clone, Debug, Deserialize, Serialize, Default)]
25#[serde(rename_all = "kebab-case")]
26pub struct RemoteWriteConfig {
27    // TODO upgrade to https
28    /// the remote_write url to post data to
29    #[serde(default = "remote_write_url")]
30    pub url: String,
31    /// username is used for posting data to the remote_write api
32    pub username: String,
33    pub password: String,
34
35    /// Sets the maximum idle connection per host allowed in the pool.
36    /// <https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.pool_max_idle_per_host>
37    #[serde(default = "pool_max_idle_per_host_default")]
38    pub pool_max_idle_per_host: usize,
39}
40
41/// DynamicPeerValidationConfig controls what sui-node, sui-bridge, and hashi-server
42/// binaries (each functioning as a node in a Sui-anchored network) we'll accept
43/// metrics from. Peer membership is determined dynamically via json-rpc calls to a
44/// full node — sui validator set and bridge committee come from fixed RPC methods,
45/// hashi membership comes from reading the on-chain `Hashi` shared object
46/// when `hashi_object_id` is set.
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 self-signed
56    /// 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 pub key
61    /// 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    /// `hashi::hashi::Hashi` shared object id. When set, the resolver discovers
68    /// hashi committee members from chain and adds them to the allowlist.
69    pub hashi_object_id: Option<String>,
70}
71
72/// StaticPeerValidationConfig, unlike the DynamicPeerValidationConfig, is not determined dynamically from rpc
73/// calls.  It instead searches a local directory for pub keys that we will add to an allow list.
74#[serde_as]
75#[derive(Clone, Debug, Deserialize, Serialize)]
76#[serde(rename_all = "kebab-case")]
77pub struct StaticPeerValidationConfig {
78    pub pub_keys: Vec<StaticPubKey>,
79}
80
81/// StaticPubKey holds a human friendly name, ip and the key file for the pub key
82/// if you don't have a valid public routable ip, use an ip from 169.254.0.0/16.
83#[serde_as]
84#[derive(Clone, Debug, Deserialize, Serialize)]
85#[serde(rename_all = "kebab-case")]
86pub struct StaticPubKey {
87    /// friendly name we will see in metrics
88    pub name: String,
89    /// the peer_id from a node config file (Ed25519 PublicKey)
90    pub peer_id: String,
91}
92
93/// the default idle worker per host (reqwest to remote write url call)
94fn pool_max_idle_per_host_default() -> usize {
95    8
96}
97
98/// the default hostname we will use if not provided
99fn hostname_default() -> Option<String> {
100    Some("localhost".to_string())
101}
102
103/// the default remote write url
104fn remote_write_url() -> String {
105    "http://metrics-gw.testnet.sui.io/api/v1/push".to_string()
106}
107
108/// load our config file from a path
109pub fn load<P: AsRef<std::path::Path>, T: DeserializeOwned + Serialize>(path: P) -> Result<T> {
110    let path = path.as_ref();
111    debug!("Reading config from {:?}", path);
112    Ok(serde_yaml::from_reader(
113        std::fs::File::open(path).context(format!("cannot open {:?}", path))?,
114    )?)
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    #[test]
121    fn config_load() {
122        const TEMPLATE: &str = include_str!("./data/config.yaml");
123
124        let _template: ProxyConfig = serde_yaml::from_str(TEMPLATE).unwrap();
125    }
126}