mod log;
mod subnet;
pub use log::*;
pub use subnet::*;
use crate::{
InternalError, InternalErrorOrigin,
cdk::candid::Principal,
ids::{CanisterRole, SubnetRole},
};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum ConfigSchemaError {
#[error("validation error: {0}")]
ValidationError(String),
}
#[cfg(any(not(target_arch = "wasm32"), test))]
pub const NAME_MAX_BYTES: usize = 40;
impl From<ConfigSchemaError> for InternalError {
fn from(err: ConfigSchemaError) -> Self {
Self::domain(InternalErrorOrigin::Config, err.to_string())
}
}
#[cfg(any(not(target_arch = "wasm32"), test))]
pub trait Validate {
fn validate(&self) -> Result<(), ConfigSchemaError>;
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigModel {
#[serde(default)]
pub fleet: Option<FleetConfig>,
#[serde(default)]
pub controllers: Vec<Principal>,
#[serde(default)]
pub standards: Option<Standards>,
#[serde(default)]
pub log: LogConfig,
#[serde(default)]
pub auth: AuthConfig,
#[serde(default)]
pub app: AppConfig,
#[serde(default)]
pub app_index: BTreeSet<CanisterRole>,
#[serde(default)]
pub subnets: BTreeMap<SubnetRole, SubnetConfig>,
}
impl ConfigModel {
#[must_use]
pub fn get_subnet(&self, role: &SubnetRole) -> Option<SubnetConfig> {
self.subnets.get(role).cloned()
}
#[cfg(test)]
#[must_use]
pub fn test_default() -> Self {
let mut cfg = Self::default();
let mut prime = SubnetConfig::default();
prime.canisters.insert(
CanisterRole::ROOT,
CanisterConfig {
kind: CanisterKind::Root,
initial_cycles: crate::cdk::types::Cycles::new(0),
topup: None,
randomness: RandomnessConfig::default(),
scaling: None,
sharding: None,
directory: None,
auth: CanisterAuthConfig::default(),
standards: StandardsCanisterConfig::default(),
metrics: MetricsCanisterConfig::default(),
},
);
cfg.subnets.insert(SubnetRole::PRIME, prime);
cfg
}
#[must_use]
pub fn is_whitelisted(&self, principal: &Principal) -> bool {
self.app
.whitelist
.as_ref()
.is_none_or(|w| w.principals.contains(&principal.to_string()))
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct FleetConfig {
#[serde(default)]
pub name: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct AppConfig {
#[serde(default)]
pub init_mode: AppInitMode,
#[serde(default)]
pub whitelist: Option<Whitelist>,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
init_mode: AppInitMode::Enabled,
whitelist: None,
}
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AppInitMode {
#[default]
Enabled,
Readonly,
Disabled,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct AuthConfig {
#[serde(default)]
pub delegated_tokens: DelegatedTokenConfig,
#[serde(default)]
pub role_attestation: RoleAttestationConfig,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct DelegatedTokenConfig {
#[serde(default = "default_delegated_tokens_enabled")]
pub enabled: bool,
#[serde(default = "default_delegated_tokens_ecdsa_key_name")]
pub ecdsa_key_name: String,
#[serde(default)]
pub max_ttl_secs: Option<u64>,
}
const fn default_delegated_tokens_enabled() -> bool {
true
}
fn default_delegated_tokens_ecdsa_key_name() -> String {
"key_1".to_string()
}
impl Default for DelegatedTokenConfig {
fn default() -> Self {
Self {
enabled: default_delegated_tokens_enabled(),
ecdsa_key_name: default_delegated_tokens_ecdsa_key_name(),
max_ttl_secs: None,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RoleAttestationConfig {
#[serde(default = "default_role_attestation_ecdsa_key_name")]
pub ecdsa_key_name: String,
#[serde(default = "default_role_attestation_max_ttl_secs")]
pub max_ttl_secs: u64,
#[serde(default)]
pub min_accepted_epoch_by_role: BTreeMap<String, u64>,
}
fn default_role_attestation_ecdsa_key_name() -> String {
"key_1".to_string()
}
const fn default_role_attestation_max_ttl_secs() -> u64 {
900
}
impl Default for RoleAttestationConfig {
fn default() -> Self {
Self {
ecdsa_key_name: default_role_attestation_ecdsa_key_name(),
max_ttl_secs: default_role_attestation_max_ttl_secs(),
min_accepted_epoch_by_role: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Whitelist {
#[serde(default)]
pub principals: BTreeSet<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Standards {
#[serde(default)]
pub icrc21: bool,
#[serde(default)]
pub icrc103: bool,
}
#[cfg(test)]
mod tests;