Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
46 changes: 46 additions & 0 deletions config/src/config/admin_service_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,50 @@ mod tests {
assert_eq!(node_config.admin_service.enabled, Some(false));
assert!(modified_config);
}

// Mainnet must reject an admin service with empty auth. Passes for Aptos
// mainnet (1); fails for Movement mainnet (126) until is_mainnet() matches 126.
fn admin_service_enabled_without_auth() -> NodeConfig {
NodeConfig {
admin_service: AdminServiceConfig {
enabled: Some(true),
..Default::default()
},
..Default::default()
}
}

// Chain 1 (Aptos mainnet) is not this network's mainnet, so the mainnet
// hardening gate does not fire and the empty auth list is accepted.
#[test]
fn admin_service_treats_aptos_mainnet_as_non_production() {
AdminServiceConfig::sanitize(
&admin_service_enabled_without_auth(),
NodeType::Validator,
Some(ChainId::new(1)), // Aptos mainnet — not this network's mainnet
)
.expect("chain 1 is not this network's mainnet; gate does not fire");
}

#[test]
fn admin_service_requires_auth_on_movement_mainnet() {
AdminServiceConfig::sanitize(
&admin_service_enabled_without_auth(),
NodeType::Validator,
Some(ChainId::new(126)),
)
.expect_err("mainnet must reject admin service with empty auth");
}

// Same as the new(1) case but via the ChainId::mainnet() helper — cross-checks
// that the number behind the `mainnet` name still resolves to a production chain.
#[test]
fn admin_service_requires_auth_on_aptos_mainnet_via_helper() {
AdminServiceConfig::sanitize(
&admin_service_enabled_without_auth(),
NodeType::Validator,
Some(ChainId::mainnet()),
)
.expect_err("mainnet must reject admin service with empty auth");
}
}
220 changes: 10 additions & 210 deletions config/src/config/config_optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@ use crate::{
config::{
node_config_loader::NodeType, utils::get_config_name, AdminServiceConfig, Error,
ExecutionConfig, IndexerConfig, InspectionServiceConfig, LoggerConfig, MempoolConfig,
NodeConfig, Peer, PeerRole, PeerSet, StateSyncConfig,
NodeConfig, StateSyncConfig,
},
network_id::NetworkId,
};
use aptos_crypto::{x25519, ValidCryptoMaterialStringExt};
use aptos_types::{chain_id::ChainId, network_address::NetworkAddress, PeerId};
use maplit::hashset;
use aptos_types::chain_id::ChainId;
use serde_yaml::Value;
use std::{collections::HashMap, str::FromStr};

// Useful optimizer constants
const OPTIMIZER_STRING: &str = "Optimizer";
Expand All @@ -27,39 +24,6 @@ const VALIDATOR_NETWORK_OPTIMIZER_NAME: &str = "ValidatorNetworkConfigOptimizer"

const IDENTITY_KEY_FILE: &str = "ephemeral_identity_key";

// Mainnet seed peers. Each seed peer entry is a tuple
// of (account address, public key, network address).
const MAINNET_SEED_PEERS: [(&str, &str, &str); 1] = [(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with the movement seed peers?

@apenzk apenzk Jul 13, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — swapped in the Movement mainnet/testnet seed peers (consensus.{mainnet,testnet}.movementnetwork.xyz).

"568fdb6acf26aae2a84419108ff13baa3ebf133844ef18e23a9f47b5af16b698",
"0x003cc2ed36e7d486539ac2c411b48d962f1ef17d884c3a7109cad43f16bd5008",
"/dns/node1.cloud-b.mainnet.aptoslabs.com/tcp/6182/noise-ik/0x003cc2ed36e7d486539ac2c411b48d962f1ef17d884c3a7109cad43f16bd5008/handshake/0",
)];

// Testnet seed peers. Each seed peer entry is a tuple
// of (account address, public key, network address).
const TESTNET_SEED_PEERS: [(&str, &str, &str); 4] = [
(
"31e55012a7d439dcd16fee0509cd5855c1fbdc62057ba7fac3f7c88f5453dd8e",
"0x87bb19b02580b7e2a91a8e9342ec77ffd8f3ad967f54e77b22aaf558c5c11755",
"/dns/seed0.testnet.aptoslabs.com/tcp/6182/noise-ik/0x87bb19b02580b7e2a91a8e9342ec77ffd8f3ad967f54e77b22aaf558c5c11755/handshake/0",
),
(
"116176e2af223a8b7f8db80dc52f7a423b4d7f8c0553a1747e92ef58849aff4f",
"0xc2f24389f31c9c18d2ceb69d153ad9299e0ea7bbd66f457e0a28ef41c77c2b64",
"/dns/seed1.testnet.aptoslabs.com/tcp/6182/noise-ik/0xc2f24389f31c9c18d2ceb69d153ad9299e0ea7bbd66f457e0a28ef41c77c2b64/handshake/0",
),
(
"12000330d7cd8a748f46c25e6ce5d236a27e13d0b510d4516ac84ecc5fddd002",
"0x171c661e5b785283978a74eafc52a906e68c73ae78119737b92f93507c753933",
"/dns/seed2.testnet.aptoslabs.com/tcp/6182/noise-ik/0x171c661e5b785283978a74eafc52a906e68c73ae78119737b92f93507c753933/handshake/0",
),
(
"03c04549114877c55f45649aba48ac0a4ff086ab7bdce3b8cc8d3d9947bc0d99",
"0xafc38bf177bd825326a1c314748612137d2b35dae6472932806806a32c23174a",
"/dns/seed3.testnet.aptoslabs.com/tcp/6182/noise-ik/0xafc38bf177bd825326a1c314748612137d2b35dae6472932806806a32c23174a/handshake/0",
),
];

/// A trait for optimizing node configs (and their sub-configs) by tweaking
/// config values based on node types, chain IDs and compiler features.
///
Expand Down Expand Up @@ -180,37 +144,20 @@ fn optimize_all_network_configs(
/// Optimize the public network config according to the node type and chain ID
fn optimize_public_network_config(
node_config: &mut NodeConfig,
local_config_yaml: &Value,
_local_config_yaml: &Value,
node_type: NodeType,
chain_id: Option<ChainId>,
_chain_id: Option<ChainId>,
) -> Result<bool, Error> {
// We only need to optimize the public network config for VFNs and PFNs
if node_type.is_validator() {
return Ok(false);
}

// Add seeds to the public network config
let mut modified_config = false;
for (index, fullnode_network_config) in node_config.full_node_networks.iter_mut().enumerate() {
let local_network_config_yaml = &local_config_yaml["full_node_networks"][index];

// Optimize the public network configs
for fullnode_network_config in node_config.full_node_networks.iter_mut() {
// No automatic seed injection. Public fullnodes configure their seeds
// via node config.
if fullnode_network_config.network_id == NetworkId::Public {
// Only add seeds to testnet and mainnet (as they are long living networks)
if local_network_config_yaml["seeds"].is_null() {
if let Some(chain_id) = chain_id {
if chain_id.is_testnet() {
fullnode_network_config.seeds =
create_seed_peers(TESTNET_SEED_PEERS.into())?;
modified_config = true;
} else if chain_id.is_mainnet() {
fullnode_network_config.seeds =
create_seed_peers(MAINNET_SEED_PEERS.into())?;
modified_config = true;
}
}
}

// If the identity key was not set in the config, attempt to
// load it from disk. Otherwise, save the already generated
// one to disk (for future runs).
Expand Down Expand Up @@ -261,61 +208,6 @@ fn optimize_validator_network_config(
Ok(modified_config)
}

/// Creates and returns a set of seed peers from the given entries
fn create_seed_peers(seed_peer_entries: Vec<(&str, &str, &str)>) -> Result<PeerSet, Error> {
// Create a map of seed peers
let mut seed_peers = HashMap::new();

// Add the seed peers
for (account_address, public_key, network_address) in seed_peer_entries {
let (peer_address, peer) = build_seed_peer(account_address, public_key, network_address)?;
seed_peers.insert(peer_address, peer);
}

Ok(seed_peers)
}

/// Builds a seed peer using the specified peer information
fn build_seed_peer(
account_address_hex: &str,
public_key_hex: &str,
network_address_str: &str,
) -> Result<(PeerId, Peer), Error> {
// Parse the account address
let account_address = PeerId::from_hex(account_address_hex).map_err(|error| {
Error::Unexpected(format!(
"Failed to parse peer account address: {:?}. Error: {:?}",
account_address_hex, error
))
})?;

// Parse the x25519 public key
let public_key = x25519::PublicKey::from_encoded_string(public_key_hex).map_err(|error| {
Error::Unexpected(format!(
"Failed to parse peer public key: {:?}. Error: {:?}",
public_key_hex, error
))
})?;

// Parse the network address string
let network_address = NetworkAddress::from_str(network_address_str).map_err(|error| {
Error::Unexpected(format!(
"Failed to parse peer network address: {:?}. Error: {:?}",
network_address_str, error
))
})?;

// Build the peer struct
let peer = Peer {
addresses: vec![network_address],
keys: hashset! {public_key},
role: PeerRole::Upstream,
};

// Return the account address and peer
Ok((account_address, peer))
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -325,10 +217,10 @@ mod tests {
},
network_id::NetworkId,
};
use aptos_crypto::{Uniform, ValidCryptoMaterial};
use aptos_types::{account_address::AccountAddress, waypoint::Waypoint};
use aptos_crypto::{x25519, Uniform, ValidCryptoMaterial};
use aptos_types::waypoint::Waypoint;
use rand::rngs::OsRng;
use std::{io::Write, path::PathBuf};
use std::{collections::HashMap, io::Write, path::PathBuf};
use tempfile::{tempdir, NamedTempFile};

fn setup_storage_config_with_temp_dir() -> (StorageConfig, PathBuf) {
Expand Down Expand Up @@ -376,98 +268,6 @@ mod tests {
assert!(!modified_config);
}

#[test]
fn test_optimize_public_network_config_mainnet() {
// Create a public network config with no seeds
let mut node_config = NodeConfig {
storage: setup_storage_config_with_temp_dir().0,
full_node_networks: vec![NetworkConfig {
network_id: NetworkId::Public,
seeds: HashMap::new(),
..Default::default()
}],
..Default::default()
};

// Optimize the public network config and verify modifications are made
let modified_config = optimize_public_network_config(
&mut node_config,
&serde_yaml::from_str("{}").unwrap(), // An empty local config
NodeType::ValidatorFullnode,
Some(ChainId::mainnet()),
)
.unwrap();
assert!(modified_config);

// Verify that the mainnet seed peers have been added to the config
let public_network_config = &node_config.full_node_networks[0];
let public_seeds = &public_network_config.seeds;
assert_eq!(public_seeds.len(), MAINNET_SEED_PEERS.len());

// Verify that the seed peers contain the expected values
for (account_address, public_key, network_address) in MAINNET_SEED_PEERS {
// Fetch the seed peer
let seed_peer = public_seeds
.get(&AccountAddress::from_hex(account_address).unwrap())
.unwrap();

// Verify the seed peer properties
assert_eq!(seed_peer.role, PeerRole::Upstream);
assert!(seed_peer
.addresses
.contains(&NetworkAddress::from_str(network_address).unwrap()));
assert!(seed_peer
.keys
.contains(&x25519::PublicKey::from_encoded_string(public_key).unwrap()));
}
}

#[test]
fn test_optimize_public_network_config_testnet() {
// Create a public network config with no seeds
let mut node_config = NodeConfig {
storage: setup_storage_config_with_temp_dir().0,
full_node_networks: vec![NetworkConfig {
network_id: NetworkId::Public,
seeds: HashMap::new(),
..Default::default()
}],
..Default::default()
};

// Optimize the public network config and verify modifications are made
let modified_config = optimize_public_network_config(
&mut node_config,
&serde_yaml::from_str("{}").unwrap(), // An empty local config
NodeType::PublicFullnode,
Some(ChainId::testnet()),
)
.unwrap();
assert!(modified_config);

// Verify that the testnet seed peers have been added to the config
let public_network_config = &node_config.full_node_networks[0];
let public_seeds = &public_network_config.seeds;
assert_eq!(public_seeds.len(), TESTNET_SEED_PEERS.len());

// Verify that the seed peers contain the expected values
for (account_address, public_key, network_address) in TESTNET_SEED_PEERS {
// Fetch the seed peer
let seed_peer = public_seeds
.get(&AccountAddress::from_hex(account_address).unwrap())
.unwrap();

// Verify the seed peer properties
assert_eq!(seed_peer.role, PeerRole::Upstream);
assert!(seed_peer
.addresses
.contains(&NetworkAddress::from_str(network_address).unwrap()));
assert!(seed_peer
.keys
.contains(&x25519::PublicKey::from_encoded_string(public_key).unwrap()));
}
}

#[test]
fn test_optimize_public_network_config_no_override() {
// Create a public network config
Expand Down
Loading
Loading