use crate::certificate_deny_config::CertificateDenyConfig;
use crate::genesis;
use crate::object_storage_config::ObjectStoreConfig;
use crate::p2p::P2pConfig;
use crate::transaction_deny_config::TransactionDenyConfig;
use crate::verifier_signing_config::VerifierSigningConfig;
use crate::Config;
use anyhow::Result;
use consensus_config::Parameters as ConsensusParameters;
use narwhal_config::Parameters as NarwhalParameters;
use once_cell::sync::OnceCell;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::collections::{BTreeMap, BTreeSet};
use std::net::SocketAddr;
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use sui_keys::keypair_file::{read_authority_keypair_from_file, read_keypair_from_file};
use sui_types::base_types::{ObjectID, SuiAddress};
use sui_types::committee::EpochId;
use sui_types::crypto::AuthorityPublicKeyBytes;
use sui_types::crypto::KeypairTraits;
use sui_types::crypto::NetworkKeyPair;
use sui_types::crypto::SuiKeyPair;
use sui_types::messages_checkpoint::CheckpointSequenceNumber;
use sui_types::supported_protocol_versions::{Chain, SupportedProtocolVersions};
use sui_types::traffic_control::{PolicyConfig, RemoteFirewallConfig};
use sui_types::crypto::{get_key_pair_from_rng, AccountKeyPair, AuthorityKeyPair};
use sui_types::multiaddr::Multiaddr;
use tracing::info;
pub const DEFAULT_GRPC_CONCURRENCY_LIMIT: usize = 20000000000;
pub const DEFAULT_VALIDATOR_GAS_PRICE: u64 = sui_types::transaction::DEFAULT_VALIDATOR_GAS_PRICE;
pub const DEFAULT_COMMISSION_RATE: u64 = 200;
#[serde_as]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct NodeConfig {
#[serde(default = "default_authority_key_pair")]
pub protocol_key_pair: AuthorityKeyPairWithPath,
#[serde(default = "default_key_pair")]
pub worker_key_pair: KeyPairWithPath,
#[serde(default = "default_key_pair")]
pub account_key_pair: KeyPairWithPath,
#[serde(default = "default_key_pair")]
pub network_key_pair: KeyPairWithPath,
pub db_path: PathBuf,
#[serde(default = "default_grpc_address")]
pub network_address: Multiaddr,
#[serde(default = "default_json_rpc_address")]
pub json_rpc_address: SocketAddr,
#[serde(default)]
pub enable_experimental_rest_api: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub rest: Option<sui_rest_api::Config>,
#[serde(default = "default_metrics_address")]
pub metrics_address: SocketAddr,
#[serde(default = "default_admin_interface_port")]
pub admin_interface_port: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub consensus_config: Option<ConsensusConfig>,
#[serde(default = "default_enable_index_processing")]
pub enable_index_processing: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub remove_deprecated_tables: bool,
#[serde(default)]
pub jsonrpc_server_type: Option<ServerType>,
#[serde(default)]
pub grpc_load_shed: Option<bool>,
#[serde(default = "default_concurrency_limit")]
pub grpc_concurrency_limit: Option<usize>,
#[serde(default)]
pub p2p_config: P2pConfig,
pub genesis: Genesis,
#[serde(default = "default_authority_store_pruning_config")]
pub authority_store_pruning_config: AuthorityStorePruningConfig,
#[serde(default = "default_end_of_epoch_broadcast_channel_capacity")]
pub end_of_epoch_broadcast_channel_capacity: usize,
#[serde(default)]
pub checkpoint_executor_config: CheckpointExecutorConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub metrics: Option<MetricsConfig>,
#[serde(skip)]
pub supported_protocol_versions: Option<SupportedProtocolVersions>,
#[serde(default)]
pub db_checkpoint_config: DBCheckpointConfig,
#[serde(default)]
pub indirect_objects_threshold: usize,
#[serde(default)]
pub expensive_safety_check_config: ExpensiveSafetyCheckConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_service_package_address: Option<SuiAddress>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_service_registry_id: Option<ObjectID>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_service_reverse_registry_id: Option<ObjectID>,
#[serde(default)]
pub transaction_deny_config: TransactionDenyConfig,
#[serde(default)]
pub certificate_deny_config: CertificateDenyConfig,
#[serde(default)]
pub state_debug_dump_config: StateDebugDumpConfig,
#[serde(default)]
pub state_archive_write_config: StateArchiveConfig,
#[serde(default)]
pub state_archive_read_config: Vec<StateArchiveConfig>,
#[serde(default)]
pub state_snapshot_write_config: StateSnapshotConfig,
#[serde(default)]
pub indexer_max_subscriptions: Option<usize>,
#[serde(default = "default_transaction_kv_store_config")]
pub transaction_kv_store_read_config: TransactionKeyValueStoreReadConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub transaction_kv_store_write_config: Option<TransactionKeyValueStoreWriteConfig>,
#[serde(default = "default_jwk_fetch_interval_seconds")]
pub jwk_fetch_interval_seconds: u64,
#[serde(default = "default_zklogin_oauth_providers")]
pub zklogin_oauth_providers: BTreeMap<Chain, BTreeSet<String>>,
#[serde(default = "default_authority_overload_config")]
pub authority_overload_config: AuthorityOverloadConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub run_with_range: Option<RunWithRange>,
#[serde(skip_serializing_if = "Option::is_none")]
pub policy_config: Option<PolicyConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub firewall_config: Option<RemoteFirewallConfig>,
#[serde(default)]
pub execution_cache: ExecutionCacheConfig,
#[serde(skip)]
#[serde(default = "bool_true")]
pub state_accumulator_v2: bool,
#[serde(default = "bool_true")]
pub enable_soft_bundle: bool,
#[serde(default = "bool_true")]
pub enable_validator_tx_finalizer: bool,
#[serde(default)]
pub verifier_signing_config: VerifierSigningConfig,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_db_write_stall: Option<bool>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum ExecutionCacheConfig {
PassthroughCache,
WritebackCache {
max_cache_size: Option<usize>,
},
}
impl Default for ExecutionCacheConfig {
fn default() -> Self {
ExecutionCacheConfig::WritebackCache {
max_cache_size: None,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ServerType {
WebSocket,
Http,
Both,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct TransactionKeyValueStoreReadConfig {
#[serde(default = "default_base_url")]
pub base_url: String,
#[serde(default = "default_cache_size")]
pub cache_size: u64,
}
impl Default for TransactionKeyValueStoreReadConfig {
fn default() -> Self {
Self {
base_url: default_base_url(),
cache_size: default_cache_size(),
}
}
}
fn default_base_url() -> String {
"https://transactions.sui.io/".to_string()
}
fn default_cache_size() -> u64 {
100_000
}
fn default_jwk_fetch_interval_seconds() -> u64 {
3600
}
pub fn default_zklogin_oauth_providers() -> BTreeMap<Chain, BTreeSet<String>> {
let mut map = BTreeMap::new();
let experimental_providers = BTreeSet::from([
"Google".to_string(),
"Facebook".to_string(),
"Twitch".to_string(),
"Kakao".to_string(),
"Apple".to_string(),
"Slack".to_string(),
"TestIssuer".to_string(),
"Microsoft".to_string(),
"KarrierOne".to_string(),
"Credenza3".to_string(),
"Playtron".to_string(),
"Threedos".to_string(),
"Onefc".to_string(),
"FanTV".to_string(),
"AwsTenant-region:us-east-1-tenant_id:us-east-1_LPSLCkC3A".to_string(), "AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), "Arden".to_string(), "AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), ]);
let providers = BTreeSet::from([
"Google".to_string(),
"Facebook".to_string(),
"Twitch".to_string(),
"Apple".to_string(),
"AwsTenant-region:us-east-1-tenant_id:us-east-1_qPsZxYqd8".to_string(), "KarrierOne".to_string(),
"Credenza3".to_string(),
"Playtron".to_string(),
"Onefc".to_string(),
"Threedos".to_string(),
"AwsTenant-region:eu-west-3-tenant_id:eu-west-3_gGVCx53Es".to_string(), "Arden".to_string(),
]);
map.insert(Chain::Mainnet, providers.clone());
map.insert(Chain::Testnet, providers);
map.insert(Chain::Unknown, experimental_providers);
map
}
fn default_transaction_kv_store_config() -> TransactionKeyValueStoreReadConfig {
TransactionKeyValueStoreReadConfig::default()
}
fn default_authority_store_pruning_config() -> AuthorityStorePruningConfig {
AuthorityStorePruningConfig::default()
}
pub fn default_enable_index_processing() -> bool {
true
}
fn default_grpc_address() -> Multiaddr {
"/ip4/0.0.0.0/tcp/8080".parse().unwrap()
}
fn default_authority_key_pair() -> AuthorityKeyPairWithPath {
AuthorityKeyPairWithPath::new(get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut OsRng).1)
}
fn default_key_pair() -> KeyPairWithPath {
KeyPairWithPath::new(
get_key_pair_from_rng::<AccountKeyPair, _>(&mut OsRng)
.1
.into(),
)
}
fn default_metrics_address() -> SocketAddr {
use std::net::{IpAddr, Ipv4Addr};
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9184)
}
pub fn default_admin_interface_port() -> u16 {
1337
}
pub fn default_json_rpc_address() -> SocketAddr {
use std::net::{IpAddr, Ipv4Addr};
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9000)
}
pub fn default_concurrency_limit() -> Option<usize> {
Some(DEFAULT_GRPC_CONCURRENCY_LIMIT)
}
pub fn default_end_of_epoch_broadcast_channel_capacity() -> usize {
128
}
pub fn bool_true() -> bool {
true
}
fn is_true(value: &bool) -> bool {
*value
}
impl Config for NodeConfig {}
impl NodeConfig {
pub fn protocol_key_pair(&self) -> &AuthorityKeyPair {
self.protocol_key_pair.authority_keypair()
}
pub fn worker_key_pair(&self) -> &NetworkKeyPair {
match self.worker_key_pair.keypair() {
SuiKeyPair::Ed25519(kp) => kp,
other => panic!(
"Invalid keypair type: {:?}, only Ed25519 is allowed for worker key",
other
),
}
}
pub fn network_key_pair(&self) -> &NetworkKeyPair {
match self.network_key_pair.keypair() {
SuiKeyPair::Ed25519(kp) => kp,
other => panic!(
"Invalid keypair type: {:?}, only Ed25519 is allowed for network key",
other
),
}
}
pub fn protocol_public_key(&self) -> AuthorityPublicKeyBytes {
self.protocol_key_pair().public().into()
}
pub fn db_path(&self) -> PathBuf {
self.db_path.join("live")
}
pub fn db_checkpoint_path(&self) -> PathBuf {
self.db_path.join("db_checkpoints")
}
pub fn archive_path(&self) -> PathBuf {
self.db_path.join("archive")
}
pub fn snapshot_path(&self) -> PathBuf {
self.db_path.join("snapshot")
}
pub fn network_address(&self) -> &Multiaddr {
&self.network_address
}
pub fn consensus_config(&self) -> Option<&ConsensusConfig> {
self.consensus_config.as_ref()
}
pub fn genesis(&self) -> Result<&genesis::Genesis> {
self.genesis.genesis()
}
pub fn sui_address(&self) -> SuiAddress {
(&self.account_key_pair.keypair().public()).into()
}
pub fn archive_reader_config(&self) -> Vec<ArchiveReaderConfig> {
self.state_archive_read_config
.iter()
.flat_map(|config| {
config
.object_store_config
.as_ref()
.map(|remote_store_config| ArchiveReaderConfig {
remote_store_config: remote_store_config.clone(),
download_concurrency: NonZeroUsize::new(config.concurrency)
.unwrap_or(NonZeroUsize::new(5).unwrap()),
use_for_pruning_watermark: config.use_for_pruning_watermark,
})
})
.collect()
}
pub fn jsonrpc_server_type(&self) -> ServerType {
self.jsonrpc_server_type.unwrap_or(ServerType::Http)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ConsensusProtocol {
#[serde(rename = "narwhal")]
Narwhal,
#[serde(rename = "mysticeti")]
Mysticeti,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ConsensusConfig {
pub db_path: PathBuf,
pub db_retention_epochs: Option<u64>,
pub db_pruner_period_secs: Option<u64>,
pub max_pending_transactions: Option<usize>,
pub max_submit_position: Option<usize>,
pub submit_delay_step_override_millis: Option<u64>,
pub address: Multiaddr,
pub narwhal_config: NarwhalParameters,
pub parameters: Option<ConsensusParameters>,
}
impl ConsensusConfig {
pub fn address(&self) -> &Multiaddr {
&self.address
}
pub fn db_path(&self) -> &Path {
&self.db_path
}
pub fn max_pending_transactions(&self) -> usize {
self.max_pending_transactions.unwrap_or(20_000)
}
pub fn submit_delay_step_override(&self) -> Option<Duration> {
self.submit_delay_step_override_millis
.map(Duration::from_millis)
}
pub fn narwhal_config(&self) -> &NarwhalParameters {
&self.narwhal_config
}
pub fn db_retention_epochs(&self) -> u64 {
self.db_retention_epochs.unwrap_or(0)
}
pub fn db_pruner_period(&self) -> Duration {
self.db_pruner_period_secs
.map(Duration::from_secs)
.unwrap_or(Duration::from_secs(3_600))
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CheckpointExecutorConfig {
#[serde(default = "default_checkpoint_execution_max_concurrency")]
pub checkpoint_execution_max_concurrency: usize,
#[serde(default = "default_local_execution_timeout_sec")]
pub local_execution_timeout_sec: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data_ingestion_dir: Option<PathBuf>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ExpensiveSafetyCheckConfig {
#[serde(default)]
enable_epoch_sui_conservation_check: bool,
#[serde(default)]
enable_deep_per_tx_sui_conservation_check: bool,
#[serde(default)]
force_disable_epoch_sui_conservation_check: bool,
#[serde(default)]
enable_state_consistency_check: bool,
#[serde(default)]
force_disable_state_consistency_check: bool,
#[serde(default)]
enable_secondary_index_checks: bool,
}
impl ExpensiveSafetyCheckConfig {
pub fn new_enable_all() -> Self {
Self {
enable_epoch_sui_conservation_check: true,
enable_deep_per_tx_sui_conservation_check: true,
force_disable_epoch_sui_conservation_check: false,
enable_state_consistency_check: true,
force_disable_state_consistency_check: false,
enable_secondary_index_checks: false, }
}
pub fn new_disable_all() -> Self {
Self {
enable_epoch_sui_conservation_check: false,
enable_deep_per_tx_sui_conservation_check: false,
force_disable_epoch_sui_conservation_check: true,
enable_state_consistency_check: false,
force_disable_state_consistency_check: true,
enable_secondary_index_checks: false,
}
}
pub fn force_disable_epoch_sui_conservation_check(&mut self) {
self.force_disable_epoch_sui_conservation_check = true;
}
pub fn enable_epoch_sui_conservation_check(&self) -> bool {
(self.enable_epoch_sui_conservation_check || cfg!(debug_assertions))
&& !self.force_disable_epoch_sui_conservation_check
}
pub fn force_disable_state_consistency_check(&mut self) {
self.force_disable_state_consistency_check = true;
}
pub fn enable_state_consistency_check(&self) -> bool {
(self.enable_state_consistency_check || cfg!(debug_assertions))
&& !self.force_disable_state_consistency_check
}
pub fn enable_deep_per_tx_sui_conservation_check(&self) -> bool {
self.enable_deep_per_tx_sui_conservation_check || cfg!(debug_assertions)
}
pub fn enable_secondary_index_checks(&self) -> bool {
self.enable_secondary_index_checks
}
}
fn default_checkpoint_execution_max_concurrency() -> usize {
200
}
fn default_local_execution_timeout_sec() -> u64 {
30
}
impl Default for CheckpointExecutorConfig {
fn default() -> Self {
Self {
checkpoint_execution_max_concurrency: default_checkpoint_execution_max_concurrency(),
local_execution_timeout_sec: default_local_execution_timeout_sec(),
data_ingestion_dir: None,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct AuthorityStorePruningConfig {
#[serde(default = "default_num_latest_epoch_dbs_to_retain")]
pub num_latest_epoch_dbs_to_retain: usize,
#[serde(default = "default_epoch_db_pruning_period_secs")]
pub epoch_db_pruning_period_secs: u64,
#[serde(default)]
pub num_epochs_to_retain: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub pruning_run_delay_seconds: Option<u64>,
#[serde(default = "default_max_checkpoints_in_batch")]
pub max_checkpoints_in_batch: usize,
#[serde(default = "default_max_transactions_in_batch")]
pub max_transactions_in_batch: usize,
#[serde(
default = "default_periodic_compaction_threshold_days",
skip_serializing_if = "Option::is_none"
)]
pub periodic_compaction_threshold_days: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub num_epochs_to_retain_for_checkpoints: Option<u64>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub killswitch_tombstone_pruning: bool,
#[serde(default = "default_smoothing", skip_serializing_if = "is_true")]
pub smooth: bool,
}
fn default_num_latest_epoch_dbs_to_retain() -> usize {
3
}
fn default_epoch_db_pruning_period_secs() -> u64 {
3600
}
fn default_max_transactions_in_batch() -> usize {
1000
}
fn default_max_checkpoints_in_batch() -> usize {
10
}
fn default_smoothing() -> bool {
cfg!(not(test))
}
fn default_periodic_compaction_threshold_days() -> Option<usize> {
Some(1)
}
impl Default for AuthorityStorePruningConfig {
fn default() -> Self {
Self {
num_latest_epoch_dbs_to_retain: default_num_latest_epoch_dbs_to_retain(),
epoch_db_pruning_period_secs: default_epoch_db_pruning_period_secs(),
num_epochs_to_retain: 0,
pruning_run_delay_seconds: if cfg!(msim) { Some(2) } else { None },
max_checkpoints_in_batch: default_max_checkpoints_in_batch(),
max_transactions_in_batch: default_max_transactions_in_batch(),
periodic_compaction_threshold_days: None,
num_epochs_to_retain_for_checkpoints: if cfg!(msim) { Some(2) } else { None },
killswitch_tombstone_pruning: false,
smooth: true,
}
}
}
impl AuthorityStorePruningConfig {
pub fn set_num_epochs_to_retain(&mut self, num_epochs_to_retain: u64) {
self.num_epochs_to_retain = num_epochs_to_retain;
}
pub fn set_num_epochs_to_retain_for_checkpoints(&mut self, num_epochs_to_retain: Option<u64>) {
self.num_epochs_to_retain_for_checkpoints = num_epochs_to_retain;
}
pub fn num_epochs_to_retain_for_checkpoints(&self) -> Option<u64> {
self.num_epochs_to_retain_for_checkpoints
.map(|n| {
if n < 2 {
info!("num_epochs_to_retain_for_checkpoints must be at least 2, rounding up from {}", n);
2
} else {
n
}
})
}
pub fn set_killswitch_tombstone_pruning(&mut self, killswitch_tombstone_pruning: bool) {
self.killswitch_tombstone_pruning = killswitch_tombstone_pruning;
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct MetricsConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub push_interval_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub push_url: Option<String>,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DBCheckpointConfig {
#[serde(default)]
pub perform_db_checkpoints_at_epoch_end: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub checkpoint_path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub object_store_config: Option<ObjectStoreConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub perform_index_db_checkpoints_at_epoch_end: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prune_and_compact_before_upload: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct ArchiveReaderConfig {
pub remote_store_config: ObjectStoreConfig,
pub download_concurrency: NonZeroUsize,
pub use_for_pruning_watermark: bool,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct StateArchiveConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub object_store_config: Option<ObjectStoreConfig>,
pub concurrency: usize,
pub use_for_pruning_watermark: bool,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct StateSnapshotConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub object_store_config: Option<ObjectStoreConfig>,
pub concurrency: usize,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct TransactionKeyValueStoreWriteConfig {
pub aws_access_key_id: String,
pub aws_secret_access_key: String,
pub aws_region: String,
pub table_name: String,
pub bucket_name: String,
pub concurrency: usize,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct AuthorityOverloadConfig {
#[serde(default = "default_max_txn_age_in_queue")]
pub max_txn_age_in_queue: Duration,
#[serde(default = "default_overload_monitor_interval")]
pub overload_monitor_interval: Duration,
#[serde(default = "default_execution_queue_latency_soft_limit")]
pub execution_queue_latency_soft_limit: Duration,
#[serde(default = "default_execution_queue_latency_hard_limit")]
pub execution_queue_latency_hard_limit: Duration,
#[serde(default = "default_max_load_shedding_percentage")]
pub max_load_shedding_percentage: u32,
#[serde(default = "default_min_load_shedding_percentage_above_hard_limit")]
pub min_load_shedding_percentage_above_hard_limit: u32,
#[serde(default = "default_safe_transaction_ready_rate")]
pub safe_transaction_ready_rate: u32,
#[serde(default = "default_check_system_overload_at_signing")]
pub check_system_overload_at_signing: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub check_system_overload_at_execution: bool,
#[serde(default = "default_max_transaction_manager_queue_length")]
pub max_transaction_manager_queue_length: usize,
#[serde(default = "default_max_transaction_manager_per_object_queue_length")]
pub max_transaction_manager_per_object_queue_length: usize,
}
fn default_max_txn_age_in_queue() -> Duration {
Duration::from_millis(200)
}
fn default_overload_monitor_interval() -> Duration {
Duration::from_secs(10)
}
fn default_execution_queue_latency_soft_limit() -> Duration {
Duration::from_secs(1)
}
fn default_execution_queue_latency_hard_limit() -> Duration {
Duration::from_secs(10)
}
fn default_max_load_shedding_percentage() -> u32 {
95
}
fn default_min_load_shedding_percentage_above_hard_limit() -> u32 {
50
}
fn default_safe_transaction_ready_rate() -> u32 {
100
}
fn default_check_system_overload_at_signing() -> bool {
true
}
fn default_max_transaction_manager_queue_length() -> usize {
100_000
}
fn default_max_transaction_manager_per_object_queue_length() -> usize {
20
}
impl Default for AuthorityOverloadConfig {
fn default() -> Self {
Self {
max_txn_age_in_queue: default_max_txn_age_in_queue(),
overload_monitor_interval: default_overload_monitor_interval(),
execution_queue_latency_soft_limit: default_execution_queue_latency_soft_limit(),
execution_queue_latency_hard_limit: default_execution_queue_latency_hard_limit(),
max_load_shedding_percentage: default_max_load_shedding_percentage(),
min_load_shedding_percentage_above_hard_limit:
default_min_load_shedding_percentage_above_hard_limit(),
safe_transaction_ready_rate: default_safe_transaction_ready_rate(),
check_system_overload_at_signing: true,
check_system_overload_at_execution: false,
max_transaction_manager_queue_length: default_max_transaction_manager_queue_length(),
max_transaction_manager_per_object_queue_length:
default_max_transaction_manager_per_object_queue_length(),
}
}
}
fn default_authority_overload_config() -> AuthorityOverloadConfig {
AuthorityOverloadConfig::default()
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
pub struct Genesis {
#[serde(flatten)]
location: GenesisLocation,
#[serde(skip)]
genesis: once_cell::sync::OnceCell<genesis::Genesis>,
}
impl Genesis {
pub fn new(genesis: genesis::Genesis) -> Self {
Self {
location: GenesisLocation::InPlace { genesis },
genesis: Default::default(),
}
}
pub fn new_from_file<P: Into<PathBuf>>(path: P) -> Self {
Self {
location: GenesisLocation::File {
genesis_file_location: path.into(),
},
genesis: Default::default(),
}
}
pub fn genesis(&self) -> Result<&genesis::Genesis> {
match &self.location {
GenesisLocation::InPlace { genesis } => Ok(genesis),
GenesisLocation::File {
genesis_file_location,
} => self
.genesis
.get_or_try_init(|| genesis::Genesis::load(genesis_file_location)),
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
#[serde(untagged)]
enum GenesisLocation {
InPlace {
genesis: genesis::Genesis,
},
File {
#[serde(rename = "genesis-file-location")]
genesis_file_location: PathBuf,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct KeyPairWithPath {
#[serde(flatten)]
location: KeyPairLocation,
#[serde(skip)]
keypair: OnceCell<Arc<SuiKeyPair>>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
#[serde_as]
#[serde(untagged)]
enum KeyPairLocation {
InPlace {
#[serde_as(as = "Arc<KeyPairBase64>")]
value: Arc<SuiKeyPair>,
},
File {
#[serde(rename = "path")]
path: PathBuf,
},
}
impl KeyPairWithPath {
pub fn new(kp: SuiKeyPair) -> Self {
let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
let arc_kp = Arc::new(kp);
cell.set(arc_kp.clone()).expect("Failed to set keypair");
Self {
location: KeyPairLocation::InPlace { value: arc_kp },
keypair: cell,
}
}
pub fn new_from_path(path: PathBuf) -> Self {
let cell: OnceCell<Arc<SuiKeyPair>> = OnceCell::new();
cell.set(Arc::new(read_keypair_from_file(&path).unwrap_or_else(
|e| panic!("Invalid keypair file at path {:?}: {e}", &path),
)))
.expect("Failed to set keypair");
Self {
location: KeyPairLocation::File { path },
keypair: cell,
}
}
pub fn keypair(&self) -> &SuiKeyPair {
self.keypair
.get_or_init(|| match &self.location {
KeyPairLocation::InPlace { value } => value.clone(),
KeyPairLocation::File { path } => {
Arc::new(
read_keypair_from_file(path).unwrap_or_else(|e| {
panic!("Invalid keypair file at path {:?}: {e}", path)
}),
)
}
})
.as_ref()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct AuthorityKeyPairWithPath {
#[serde(flatten)]
location: AuthorityKeyPairLocation,
#[serde(skip)]
keypair: OnceCell<Arc<AuthorityKeyPair>>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq)]
#[serde_as]
#[serde(untagged)]
enum AuthorityKeyPairLocation {
InPlace { value: Arc<AuthorityKeyPair> },
File { path: PathBuf },
}
impl AuthorityKeyPairWithPath {
pub fn new(kp: AuthorityKeyPair) -> Self {
let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
let arc_kp = Arc::new(kp);
cell.set(arc_kp.clone())
.expect("Failed to set authority keypair");
Self {
location: AuthorityKeyPairLocation::InPlace { value: arc_kp },
keypair: cell,
}
}
pub fn new_from_path(path: PathBuf) -> Self {
let cell: OnceCell<Arc<AuthorityKeyPair>> = OnceCell::new();
cell.set(Arc::new(
read_authority_keypair_from_file(&path)
.unwrap_or_else(|_| panic!("Invalid authority keypair file at path {:?}", &path)),
))
.expect("Failed to set authority keypair");
Self {
location: AuthorityKeyPairLocation::File { path },
keypair: cell,
}
}
pub fn authority_keypair(&self) -> &AuthorityKeyPair {
self.keypair
.get_or_init(|| match &self.location {
AuthorityKeyPairLocation::InPlace { value } => value.clone(),
AuthorityKeyPairLocation::File { path } => {
Arc::new(
read_authority_keypair_from_file(path).unwrap_or_else(|_| {
panic!("Invalid authority keypair file {:?}", &path)
}),
)
}
})
.as_ref()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct StateDebugDumpConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub dump_file_directory: Option<PathBuf>,
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use fastcrypto::traits::KeyPair;
use rand::{rngs::StdRng, SeedableRng};
use sui_keys::keypair_file::{write_authority_keypair_to_file, write_keypair_to_file};
use sui_types::crypto::{get_key_pair_from_rng, AuthorityKeyPair, NetworkKeyPair, SuiKeyPair};
use super::Genesis;
use crate::NodeConfig;
#[test]
fn serialize_genesis_from_file() {
let g = Genesis::new_from_file("path/to/file");
let s = serde_yaml::to_string(&g).unwrap();
assert_eq!("---\ngenesis-file-location: path/to/file\n", s);
let loaded_genesis: Genesis = serde_yaml::from_str(&s).unwrap();
assert_eq!(g, loaded_genesis);
}
#[test]
fn fullnode_template() {
const TEMPLATE: &str = include_str!("../data/fullnode-template.yaml");
let _template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
}
#[test]
fn load_key_pairs_to_node_config() {
let protocol_key_pair: AuthorityKeyPair =
get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
let worker_key_pair: NetworkKeyPair =
get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
let network_key_pair: NetworkKeyPair =
get_key_pair_from_rng(&mut StdRng::from_seed([0; 32])).1;
write_authority_keypair_to_file(&protocol_key_pair, PathBuf::from("protocol.key")).unwrap();
write_keypair_to_file(
&SuiKeyPair::Ed25519(worker_key_pair.copy()),
PathBuf::from("worker.key"),
)
.unwrap();
write_keypair_to_file(
&SuiKeyPair::Ed25519(network_key_pair.copy()),
PathBuf::from("network.key"),
)
.unwrap();
const TEMPLATE: &str = include_str!("../data/fullnode-template-with-path.yaml");
let template: NodeConfig = serde_yaml::from_str(TEMPLATE).unwrap();
assert_eq!(
template.protocol_key_pair().public(),
protocol_key_pair.public()
);
assert_eq!(
template.network_key_pair().public(),
network_key_pair.public()
);
assert_eq!(
template.worker_key_pair().public(),
worker_key_pair.public()
);
}
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum RunWithRange {
Epoch(EpochId),
Checkpoint(CheckpointSequenceNumber),
}
impl RunWithRange {
pub fn is_epoch_gt(&self, epoch_id: EpochId) -> bool {
matches!(self, RunWithRange::Epoch(e) if epoch_id > *e)
}
pub fn matches_checkpoint(&self, seq_num: CheckpointSequenceNumber) -> bool {
matches!(self, RunWithRange::Checkpoint(seq) if *seq == seq_num)
}
}