Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
711 changes: 212 additions & 499 deletions crates/node/src/cli.rs

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions crates/node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ use std::{
path::Path,
};

mod start;
pub use start::{
GcpStartConfig, SecretsStartConfig, StartConfig, TeeAuthorityStartConfig, TeeStartConfig,
};

mod foreign_chains;
pub use foreign_chains::{
AbstractApiVariant, AbstractChainConfig, AbstractProviderConfig, AuthConfig, BitcoinApiVariant,
Expand Down
4 changes: 4 additions & 0 deletions crates/node/src/config/foreign_chains/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ pub enum TokenConfig {
impl TokenConfig {
pub fn resolve(&self) -> anyhow::Result<String> {
match self {
// TODO(#2335): do not resolve env variables this deep in the binary.
// Should be resolved at start, preferably in the config so we can kill env configs
//
// One option is to have a separate secrets config file.
TokenConfig::Env { env } => {
std::env::var(env).with_context(|| format!("environment variable {env} is not set"))
}
Expand Down
128 changes: 128 additions & 0 deletions crates/node/src/config/start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use super::ConfigFile;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tee_authority::tee_authority::{
DstackTeeAuthorityConfig, LocalTeeAuthorityConfig, TeeAuthority, DEFAULT_DSTACK_ENDPOINT,
DEFAULT_PHALA_TDX_QUOTE_UPLOAD_URL,
};
use url::Url;

/// Configuration for starting the MPC node. This is the canonical type used
/// by the run logic. Both `StartCmd` (CLI flags) and `StartWithConfigFileCmd`
/// (JSON file) convert into this type.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StartConfig {
pub home_dir: PathBuf,
/// Encryption keys and backup settings.
pub secrets: SecretsStartConfig,
/// TEE authority and image hash monitoring settings.
pub tee: TeeStartConfig,
/// GCP keyshare storage settings. Optional — omit if not using GCP.
#[serde(default)]
pub gcp: Option<GcpStartConfig>,
/// Node configuration (indexer, protocol parameters, etc.).
pub node: ConfigFile,
}

/// Encryption keys needed at startup.
#[derive(Clone, Serialize, Deserialize)]
pub struct SecretsStartConfig {
/// Hex-encoded 16 byte AES key for local storage encryption.
pub secret_store_key_hex: String,
/// Hex-encoded 32 byte AES key for backup encryption.
/// If not provided, a key is generated and written to disk.
#[serde(default)]
pub backup_encryption_key_hex: Option<String>,
}

impl std::fmt::Debug for SecretsStartConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SecretsStartConfig")
.field("secret_store_key_hex", &"[REDACTED]")
.field(
"backup_encryption_key_hex",
&self
.backup_encryption_key_hex
.as_ref()
.map(|_| "[REDACTED]"),
)
.finish()
}
}

/// TEE-related configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TeeStartConfig {
/// TEE authority configuration.
pub authority: TeeAuthorityStartConfig,
/// Hex representation of the hash of the running image. Only required in TEE.
#[serde(default)]
pub image_hash: Option<String>,
/// Path to the file where the node writes the latest allowed hash.
/// If not set, assumes running outside of TEE and skips image hash monitoring.
#[serde(default)]
pub latest_allowed_hash_file: Option<PathBuf>,
}

/// GCP keyshare storage configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GcpStartConfig {
/// GCP secret ID for storing the root keyshare.
pub keyshare_secret_id: String,
/// GCP project ID.
pub project_id: String,
}

/// TEE authority configuration for JSON deserialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TeeAuthorityStartConfig {
Local,
Dstack {
#[serde(default = "default_dstack_endpoint")]
dstack_endpoint: String,
#[serde(default = "default_quote_upload_url")]
// TODO(#2333): use URL type for this type
quote_upload_url: String,
},
}

fn default_dstack_endpoint() -> String {
DEFAULT_DSTACK_ENDPOINT.to_string()
}

fn default_quote_upload_url() -> String {
DEFAULT_PHALA_TDX_QUOTE_UPLOAD_URL.to_string()
}

impl TeeAuthorityStartConfig {
pub fn into_tee_authority(self) -> anyhow::Result<TeeAuthority> {
Ok(match self {
TeeAuthorityStartConfig::Local => LocalTeeAuthorityConfig::default().into(),
TeeAuthorityStartConfig::Dstack {
dstack_endpoint,
quote_upload_url,
} => {
let url: Url = quote_upload_url
.parse()
.context("invalid quote_upload_url")?;
DstackTeeAuthorityConfig::new(dstack_endpoint, url).into()
}
})
}
}

impl StartConfig {
pub fn from_json_file(path: &std::path::Path) -> anyhow::Result<Self> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("failed to read config file: {}", path.display()))?;
let config: Self = serde_json::from_str(&content)
.with_context(|| format!("failed to parse config file: {}", path.display()))?;
config
.node
.validate()
.context("invalid node config in config file")?;
Ok(config)
}
}
1 change: 1 addition & 0 deletions crates/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod protocol;
mod protocol_version;
mod providers;
pub mod requests;
mod run;
mod runtime;
mod storage;
pub mod tracing;
Expand Down
Loading
Loading