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 binaries that are functioning as a validator that we'll speak with.
42/// Peer in this case is peers within the consensus committee, for each epoch.  This membership is determined dynamically
43/// for each epoch via json-rpc calls to a full node.
44#[serde_as]
45#[derive(Clone, Debug, Deserialize, Serialize)]
46#[serde(rename_all = "kebab-case")]
47pub struct DynamicPeerValidationConfig {
48    /// url is the json-rpc url we use to obtain valid peers on the blockchain
49    pub url: String,
50    #[serde_as(as = "DurationSeconds<u64>")]
51    pub interval: Duration,
52    /// if certificate_file and private_key are not provided, we'll create a self-signed
53    /// cert using this hostname
54    #[serde(default = "hostname_default")]
55    pub hostname: Option<String>,
56
57    /// incoming client connections to this proxy will be presented with this pub key
58    /// please use an absolute path
59    pub certificate_file: Option<String>,
60    /// private key for tls
61    /// please use an absolute path
62    pub private_key: Option<String>,
63}
64
65/// StaticPeerValidationConfig, unlike the DynamicPeerValidationConfig, is not determined dynamically from rpc
66/// calls.  It instead searches a local directory for pub keys that we will add to an allow list.
67#[serde_as]
68#[derive(Clone, Debug, Deserialize, Serialize)]
69#[serde(rename_all = "kebab-case")]
70pub struct StaticPeerValidationConfig {
71    pub pub_keys: Vec<StaticPubKey>,
72}
73
74/// StaticPubKey holds a human friendly name, ip and the key file for the pub key
75/// if you don't have a valid public routable ip, use an ip from 169.254.0.0/16.
76#[serde_as]
77#[derive(Clone, Debug, Deserialize, Serialize)]
78#[serde(rename_all = "kebab-case")]
79pub struct StaticPubKey {
80    /// friendly name we will see in metrics
81    pub name: String,
82    /// the peer_id from a node config file (Ed25519 PublicKey)
83    pub peer_id: String,
84}
85
86/// the default idle worker per host (reqwest to remote write url call)
87fn pool_max_idle_per_host_default() -> usize {
88    8
89}
90
91/// the default hostname we will use if not provided
92fn hostname_default() -> Option<String> {
93    Some("localhost".to_string())
94}
95
96/// the default remote write url
97fn remote_write_url() -> String {
98    "http://metrics-gw.testnet.sui.io/api/v1/push".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}