diff --git a/Cargo.lock b/Cargo.lock index 4db8c90c8f797..92bf53f7ce384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,6 +1325,9 @@ name = "debug-ignore" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e51694c5f8b91baf933e6429a3df4ff3e9f1160386d150790b97bef73337d1b" +dependencies = [ + "serde 1.0.137", +] [[package]] name = "derivative" @@ -5438,6 +5441,7 @@ dependencies = [ "strum", "strum_macros", "sui-adapter", + "sui-config", "sui-framework", "sui-network", "sui-open-rpc", @@ -5486,6 +5490,24 @@ dependencies = [ "sui-transactional-test-runner", ] +[[package]] +name = "sui-config" +version = "0.0.0" +dependencies = [ + "anyhow", + "config 0.1.0", + "crypto", + "debug-ignore", + "dirs", + "multiaddr", + "rand 0.7.3", + "serde 1.0.137", + "serde_json", + "sui-framework", + "sui-types", + "tracing", +] + [[package]] name = "sui-faucet" version = "0.1.0" @@ -5500,6 +5522,7 @@ dependencies = [ "sui", "sui-types", "tempfile", + "test_utils", "thiserror", "tokio", "tower", @@ -5879,6 +5902,7 @@ dependencies = [ name = "test_utils" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "bcs", "bytes", @@ -5890,6 +5914,7 @@ dependencies = [ "rocksdb", "sui", "sui-adapter", + "sui-config", "sui-framework", "sui-network", "sui-types", diff --git a/Cargo.toml b/Cargo.toml index 48e60cf372002..723f08e779a9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "crates/sui-config", "crates/sui-network", "crates/x", "faucet", diff --git a/crates/sui-config/Cargo.toml b/crates/sui-config/Cargo.toml new file mode 100644 index 0000000000000..e6d2335ca7e43 --- /dev/null +++ b/crates/sui-config/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sui-config" +version = "0.0.0" +authors = ["Mysten Labs "] +license = "Apache-2.0" +publish = false +edition = "2021" + +[dependencies] +anyhow = { version = "1.0.57", features = ["backtrace"] } +serde = { version = "1.0.137", features = ["derive"] } +serde_json = "1.0.80" +rand = "0.7.3" +dirs = "4.0.0" +multiaddr = "0.14.0" +debug-ignore = { version = "1.0.2", features = ["serde"] } +tracing = "0.1.34" + +narwhal-config = { git = "https://github.com/MystenLabs/narwhal", rev = "23745f48103656eae4a4205d0b3edd53ad8894de", package = "config" } +narwhal-crypto = { git = "https://github.com/MystenLabs/narwhal", rev = "23745f48103656eae4a4205d0b3edd53ad8894de", package = "crypto" } + +sui-framework = { path = "../../sui_programmability/framework" } +sui-types = { path = "../../sui_types" } diff --git a/crates/sui-config/src/lib.rs b/crates/sui-config/src/lib.rs new file mode 100644 index 0000000000000..f57823696cd92 --- /dev/null +++ b/crates/sui-config/src/lib.rs @@ -0,0 +1,501 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use debug_ignore::DebugIgnore; +use multiaddr::Multiaddr; +use narwhal_config::Parameters as ConsensusParameters; +use narwhal_config::{ + Authority, Committee as ConsensusCommittee, PrimaryAddresses, Stake, WorkerAddresses, +}; +use narwhal_crypto::ed25519::Ed25519PublicKey; +use rand::rngs::OsRng; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::{Path, PathBuf}; +use sui_framework::DEFAULT_FRAMEWORK_PATH; +use sui_types::base_types::{encode_bytes_hex, ObjectID, SuiAddress}; +use sui_types::committee::{Committee, EpochId}; +use sui_types::crypto::{get_key_pair_from_rng, KeyPair, PublicKeyBytes}; +use tracing::trace; + +pub mod utils; + +const DEFAULT_STAKE: usize = 1; + +#[derive(Debug, Deserialize, Serialize)] +pub struct ValidatorConfig { + key_pair: KeyPair, + db_path: PathBuf, + network_address: Multiaddr, + metrics_address: Multiaddr, + + consensus_config: ConsensuseConfig, + committee_config: CommitteeConfig, +} + +impl Config for ValidatorConfig {} + +impl ValidatorConfig { + pub fn key_pair(&self) -> &KeyPair { + &self.key_pair + } + + pub fn public_key(&self) -> PublicKeyBytes { + *self.key_pair.public_key_bytes() + } + + pub fn sui_address(&self) -> SuiAddress { + SuiAddress::from(self.public_key()) + } + + pub fn db_path(&self) -> &Path { + &self.db_path + } + + pub fn network_address(&self) -> &Multiaddr { + &self.network_address + } + + pub fn consensus_config(&self) -> &ConsensuseConfig { + &self.consensus_config + } + + pub fn committee_config(&self) -> &CommitteeConfig { + &self.committee_config + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ConsensuseConfig { + consensus_address: Multiaddr, + consensus_db_path: PathBuf, + + //TODO make narwhal config serializable + #[serde(skip_serializing)] + #[serde(default)] + narwhal_config: DebugIgnore, +} + +impl ConsensuseConfig { + pub fn address(&self) -> &Multiaddr { + &self.consensus_address + } + + pub fn db_path(&self) -> &Path { + &self.consensus_db_path + } + + pub fn narwhal_config(&self) -> &ConsensusParameters { + &self.narwhal_config + } +} + +//TODO get this information from on-chain + some way to do network discovery +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct CommitteeConfig { + epoch: EpochId, + validator_set: Vec, + consensus_committee: DebugIgnore>, +} + +impl CommitteeConfig { + pub fn epoch(&self) -> EpochId { + self.epoch + } + + pub fn validator_set(&self) -> &[ValidatorInfo] { + &self.validator_set + } + + pub fn narwhal_committee(&self) -> &ConsensusCommittee { + &self.consensus_committee + } + + pub fn committee(&self) -> Committee { + let voting_rights = self + .validator_set() + .iter() + .map(|validator| (validator.public_key(), validator.stake())) + .collect(); + Committee::new(self.epoch(), voting_rights) + } +} + +/// Publicly known information about a validator +/// TODO read most of this from on-chain +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ValidatorInfo { + public_key: PublicKeyBytes, + stake: usize, + network_address: Multiaddr, +} + +impl ValidatorInfo { + pub fn sui_address(&self) -> SuiAddress { + SuiAddress::from(self.public_key()) + } + + pub fn public_key(&self) -> PublicKeyBytes { + self.public_key + } + + pub fn stake(&self) -> usize { + self.stake + } + + pub fn network_address(&self) -> &Multiaddr { + &self.network_address + } +} + +/// This is a config that is used for testing or local use as it contains the config and keys for +/// all validators +#[derive(Debug, Deserialize, Serialize)] +pub struct NetworkConfig { + validator_configs: Vec, + loaded_move_packages: Vec<(PathBuf, ObjectID)>, +} + +impl Config for NetworkConfig {} + +impl NetworkConfig { + pub fn validator_configs(&self) -> &[ValidatorConfig] { + &self.validator_configs + } + + pub fn loaded_move_packages(&self) -> &[(PathBuf, ObjectID)] { + &self.loaded_move_packages + } + + pub fn add_move_package(&mut self, path: PathBuf, object_id: ObjectID) { + self.loaded_move_packages.push((path, object_id)) + } + + pub fn validator_set(&self) -> &[ValidatorInfo] { + self.validator_configs()[0] + .committee_config() + .validator_set() + } + + pub fn committee(&self) -> Committee { + self.validator_configs()[0].committee_config().committee() + } + + pub fn into_validator_configs(self) -> Vec { + self.validator_configs + } + + pub fn generate_with_rng( + config_dir: &Path, + quorum_size: usize, + mut rng: R, + ) -> Self { + let epoch = 0; + + let keys = (0..quorum_size) + .map(|_| get_key_pair_from_rng(&mut rng).1) + .collect::>(); + + let validator_set = keys + .iter() + .map(|key| { + let public_key = *key.public_key_bytes(); + let stake = DEFAULT_STAKE; + let network_address = new_network_address(); + + ValidatorInfo { + public_key, + stake, + network_address, + } + }) + .collect::>(); + + let narwhal_committee = validator_set + .iter() + .map(|validator| { + let name = validator + .public_key + .make_narwhal_public_key() + .expect("Can't get narwhal public key"); + let primary = PrimaryAddresses { + primary_to_primary: new_network_address(), + worker_to_primary: new_network_address(), + }; + let workers = [( + 0, // worker_id + WorkerAddresses { + primary_to_worker: new_network_address(), + transactions: new_network_address(), + worker_to_worker: new_network_address(), + }, + )] + .into_iter() + .collect(); + let authority = Authority { + stake: validator.stake() as Stake, //TODO this should at least be the same size integer + primary, + workers, + }; + + (name, authority) + }) + .collect(); + let consensus_committee = ConsensusCommittee { + authorities: narwhal_committee, + }; + + let committe_config = CommitteeConfig { + epoch, + validator_set, + consensus_committee: DebugIgnore(consensus_committee), + }; + + let validator_configs = keys + .into_iter() + .map(|key| { + let db_path = config_dir + .join(AUTHORITIES_DB_NAME) + .join(encode_bytes_hex(key.public_key_bytes())); + let network_address = committe_config + .validator_set() + .iter() + .find(|validator| validator.public_key() == *key.public_key_bytes()) + .map(|validator| validator.network_address().clone()) + .unwrap(); + let consensus_address = committe_config + .narwhal_committee() + .authorities + .get(&key.public_key_bytes().make_narwhal_public_key().unwrap()) + .unwrap() + .workers + .get(&0) + .unwrap() + .transactions + .clone(); + let consensus_db_path = config_dir + .join(CONSENSUS_DB_NAME) + .join(encode_bytes_hex(key.public_key_bytes())); + let consensus_config = ConsensuseConfig { + consensus_address, + consensus_db_path, + narwhal_config: Default::default(), + }; + + let metrics_address = new_network_address(); + + ValidatorConfig { + key_pair: key, + db_path, + network_address, + metrics_address, + consensus_config, + committee_config: committe_config.clone(), + } + }) + .collect(); + + Self { + validator_configs, + loaded_move_packages: vec![], + } + } + + pub fn generate(config_dir: &Path, quorum_size: usize) -> Self { + Self::generate_with_rng(config_dir, quorum_size, OsRng) + } +} + +// pub struct ConfigBuilder { +// rng: R, + +// } + +fn new_network_address() -> Multiaddr { + format!("/dns/localhost/tcp/{}/http", utils::get_available_port()) + .parse() + .unwrap() +} + +const SUI_DIR: &str = ".sui"; +const SUI_CONFIG_DIR: &str = "sui_config"; +pub const SUI_NETWORK_CONFIG: &str = "network.conf"; +pub const SUI_WALLET_CONFIG: &str = "wallet.conf"; +pub const SUI_GATEWAY_CONFIG: &str = "gateway.conf"; +pub const SUI_DEV_NET_URL: &str = "https://gateway.devnet.sui.io:9000"; + +pub const AUTHORITIES_DB_NAME: &str = "authorities_db"; +pub const DEFAULT_STARTING_PORT: u16 = 10000; +pub const CONSENSUS_DB_NAME: &str = "consensus_db"; + +pub fn sui_config_dir() -> Result { + match std::env::var_os("SUI_CONFIG_DIR") { + Some(config_env) => Ok(config_env.into()), + None => match dirs::home_dir() { + Some(v) => Ok(v.join(SUI_DIR).join(SUI_CONFIG_DIR)), + None => anyhow::bail!("Cannot obtain home directory path"), + }, + } + .and_then(|dir| { + if !dir.exists() { + std::fs::create_dir_all(dir.clone())?; + } + Ok(dir) + }) +} + +#[derive(Serialize, Deserialize)] +pub struct GenesisConfig { + pub committee_size: usize, + pub accounts: Vec, + pub move_packages: Vec, + pub sui_framework_lib_path: PathBuf, + pub move_framework_lib_path: PathBuf, +} + +impl Config for GenesisConfig {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AccountConfig { + #[serde( + skip_serializing_if = "Option::is_none", + serialize_with = "SuiAddress::optional_address_as_hex", + deserialize_with = "SuiAddress::optional_address_from_hex" + )] + pub address: Option, + pub gas_objects: Vec, + pub gas_object_ranges: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ObjectConfigRange { + /// Starting object id + pub offset: ObjectID, + /// Number of object ids + pub count: u64, + /// Gas value per object id + pub gas_value: u64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ObjectConfig { + #[serde(default = "ObjectID::random")] + pub object_id: ObjectID, + #[serde(default = "default_gas_value")] + pub gas_value: u64, +} + +fn default_gas_value() -> u64 { + DEFAULT_GAS_AMOUNT +} + +const DEFAULT_GAS_AMOUNT: u64 = 100000; +const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4; +const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5; +const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5; + +impl GenesisConfig { + pub fn for_local_testing() -> Result { + Self::custom_genesis( + DEFAULT_NUMBER_OF_AUTHORITIES, + DEFAULT_NUMBER_OF_ACCOUNT, + DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT, + ) + } + + pub fn custom_genesis( + num_authorities: usize, + num_accounts: usize, + num_objects_per_account: usize, + ) -> Result { + assert!( + num_authorities > 0, + "num_authorities should be larger than 0" + ); + + let mut accounts = Vec::new(); + for _ in 0..num_accounts { + let mut objects = Vec::new(); + for _ in 0..num_objects_per_account { + objects.push(ObjectConfig { + object_id: ObjectID::random(), + gas_value: DEFAULT_GAS_AMOUNT, + }) + } + accounts.push(AccountConfig { + address: None, + gas_objects: objects, + gas_object_ranges: Some(Vec::new()), + }) + } + + Ok(Self { + accounts, + ..Default::default() + }) + } +} + +impl Default for GenesisConfig { + fn default() -> Self { + Self { + committee_size: DEFAULT_NUMBER_OF_AUTHORITIES, + accounts: vec![], + move_packages: vec![], + sui_framework_lib_path: PathBuf::from(DEFAULT_FRAMEWORK_PATH), + move_framework_lib_path: PathBuf::from(DEFAULT_FRAMEWORK_PATH) + .join("deps") + .join("move-stdlib"), + } + } +} + +pub trait Config +where + Self: DeserializeOwned + Serialize, +{ + fn persisted(self, path: &Path) -> PersistedConfig { + PersistedConfig { + inner: self, + path: path.to_path_buf(), + } + } +} + +pub struct PersistedConfig { + inner: C, + path: PathBuf, +} + +impl PersistedConfig +where + C: Config, +{ + pub fn read(path: &Path) -> Result { + trace!("Reading config from '{:?}'", path); + let reader = fs::File::open(path)?; + Ok(serde_json::from_reader(reader)?) + } + + pub fn save(&self) -> Result<(), anyhow::Error> { + trace!("Writing config to '{:?}'", &self.path); + let config = serde_json::to_string_pretty(&self.inner)?; + fs::write(&self.path, config)?; + Ok(()) + } +} + +impl std::ops::Deref for PersistedConfig { + type Target = C; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl std::ops::DerefMut for PersistedConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} diff --git a/sui/src/config/utils.rs b/crates/sui-config/src/utils.rs similarity index 100% rename from sui/src/config/utils.rs rename to crates/sui-config/src/utils.rs diff --git a/faucet/Cargo.toml b/faucet/Cargo.toml index 21af3a74d3a45..1f5815e015494 100644 --- a/faucet/Cargo.toml +++ b/faucet/Cargo.toml @@ -25,6 +25,7 @@ sui-types = { path = "../sui_types" } [dev-dependencies] tempfile = "3.3.0" +test_utils = { path = "../test_utils" } [[bin]] name = "sui-faucet" diff --git a/faucet/src/lib.rs b/faucet/src/lib.rs index 598d33a717514..537b1a6daaaad 100644 --- a/faucet/src/lib.rs +++ b/faucet/src/lib.rs @@ -15,4 +15,4 @@ pub use responses::*; mod test_utils; #[cfg(test)] -pub use test_utils::*; +pub use crate::test_utils::*; diff --git a/faucet/src/test_utils.rs b/faucet/src/test_utils.rs index 8b92d480546bc..22aa84e137aee 100644 --- a/faucet/src/test_utils.rs +++ b/faucet/src/test_utils.rs @@ -1,33 +1,19 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, bail}; -use std::path::Path; use sui::{ - config::{ - Config, GatewayConfig, GatewayType, GenesisConfig, WalletConfig, SUI_GATEWAY_CONFIG, - SUI_NETWORK_CONFIG, SUI_WALLET_CONFIG, - }, - keystore::KeystoreType, - sui_commands::{genesis, SuiNetwork}, + config::SUI_WALLET_CONFIG, + sui_commands::SuiNetwork, wallet_commands::{WalletCommands, WalletContext}, }; -use sui_types::{ - base_types::SuiAddress, - crypto::{random_key_pairs, KeyPair}, -}; - -/* -------------------------------------------------------------------------- */ -/* NOTE: Below is copied from sui/src/unit_tests, we should consolidate them */ -/* -------------------------------------------------------------------------- */ - -const NUM_VALIDATOR: usize = 4; +use sui_types::base_types::SuiAddress; +use test_utils::network::start_test_network; pub async fn setup_network_and_wallet( ) -> Result<(SuiNetwork, WalletContext, SuiAddress), anyhow::Error> { let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; + let network = start_test_network(working_dir.path(), None).await?; // Create Wallet context. let wallet_conf = working_dir.path().join(SUI_WALLET_CONFIG); @@ -42,87 +28,3 @@ pub async fn setup_network_and_wallet( .await?; Ok((network, context, address)) } - -pub async fn start_test_network( - working_dir: &Path, - genesis_config: Option, - key_pairs: Option>, -) -> Result { - let working_dir = working_dir.to_path_buf(); - let network_path = working_dir.join(SUI_NETWORK_CONFIG); - let wallet_path = working_dir.join(SUI_WALLET_CONFIG); - let keystore_path = working_dir.join("wallet.key"); - let db_folder_path = working_dir.join("client_db"); - - if genesis_config.is_none() ^ key_pairs.is_none() { - return Err(anyhow!( - "genesis_config and key_pairs should be absent/present in tandem." - )); - } - let key_pairs = key_pairs.unwrap_or_else(|| random_key_pairs(NUM_VALIDATOR)); - - let mut genesis_config = match genesis_config { - Some(genesis_config) => genesis_config, - None => GenesisConfig::default_genesis( - &working_dir, - Some(( - key_pairs - .iter() - .map(|kp| *kp.public_key_bytes()) - .collect::>(), - key_pairs[0].copy(), - )), - )?, - }; - if genesis_config.authorities.len() != key_pairs.len() { - bail!("genesis_config's authority num should match key_pairs's length."); - } - - let authorities = genesis_config.authorities.into_iter().collect(); - genesis_config.authorities = authorities; - - let (network_config, accounts, mut keystore) = genesis(genesis_config, None).await?; - let key_pair_refs = key_pairs.iter().collect::>(); - let network = SuiNetwork::start(&network_config, key_pair_refs).await?; - - let network_config = network_config.persisted(&network_path); - network_config.save()?; - keystore.set_path(&keystore_path); - keystore.save()?; - - let authorities = network_config.get_authority_infos(); - let authorities = authorities - .into_iter() - .zip(&network.spawned_authorities) - .map(|(mut info, server)| { - info.network_address = server.address().to_owned(); - info - }) - .collect::>(); - let active_address = accounts.get(0).copied(); - - GatewayConfig { - db_folder_path: db_folder_path.clone(), - authorities: authorities.clone(), - ..Default::default() - } - .persisted(&working_dir.join(SUI_GATEWAY_CONFIG)) - .save()?; - - // Create wallet config with stated authorities port - WalletConfig { - accounts, - keystore: KeystoreType::File(keystore_path), - gateway: GatewayType::Embedded(GatewayConfig { - db_folder_path, - authorities, - ..Default::default() - }), - active_address, - } - .persisted(&wallet_path) - .save()?; - - // Return network handle - Ok(network) -} diff --git a/sui/Cargo.toml b/sui/Cargo.toml index 978b692bc6edf..69229bead5cd3 100644 --- a/sui/Cargo.toml +++ b/sui/Cargo.toml @@ -42,6 +42,7 @@ sui_core = { path = "../sui_core" } sui-adapter = { path = "../sui_programmability/adapter" } sui-framework = { path = "../sui_programmability/framework" } sui-network = { path = "../crates/sui-network" } +sui-config = { path = "../crates/sui-config" } sui-types = { path = "../sui_types" } sui-verifier = { path = "../sui_programmability/verifier" } sui-open-rpc = { path = "open_rpc" } diff --git a/sui/src/benchmark/bench_types.rs b/sui/src/benchmark/bench_types.rs index 1731713cbe154..3eea5379acda6 100644 --- a/sui/src/benchmark/bench_types.rs +++ b/sui/src/benchmark/bench_types.rs @@ -1,7 +1,8 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::config::{Config, NetworkConfig}; +use crate::config::Config; +use sui_config::NetworkConfig; use super::load_generator::calculate_throughput; use clap::*; diff --git a/sui/src/benchmark/load_generator.rs b/sui/src/benchmark/load_generator.rs index 68cc06aea6f77..f3a29805a6bfa 100644 --- a/sui/src/benchmark/load_generator.rs +++ b/sui/src/benchmark/load_generator.rs @@ -19,11 +19,11 @@ use sui_core::{ authority_client::{AuthorityAPI, NetworkAuthorityClient}, authority_server::{AuthorityServer, AuthorityServerHandle}, }; -use sui_types::{committee::Committee, messages::*}; +use sui_types::messages::*; use tokio::{sync::Notify, time}; use tracing::{error, info}; -use crate::config::NetworkConfig; +use sui_config::NetworkConfig; pub fn check_transaction_response(reply_message: Result) { match reply_message { @@ -452,11 +452,11 @@ impl MultiFixedRateLoadGenerator { network_cfg: &NetworkConfig, ) -> Self { let network_clients_stake: Vec<(Multiaddr, usize)> = network_cfg - .authorities + .validator_set() .iter() - .map(|q| (q.network_address.clone(), q.stake)) + .map(|q| (q.network_address().to_owned(), q.stake())) .collect(); - let committee_quorum_threshold = Committee::from(network_cfg).quorum_threshold(); + let committee_quorum_threshold = network_cfg.committee().quorum_threshold(); let mut handles = vec![]; let tick_notifier = Arc::new(Notify::new()); diff --git a/sui/src/benchmark/transaction_creator.rs b/sui/src/benchmark/transaction_creator.rs index 925f429e0e071..db5f4ee8dd269 100644 --- a/sui/src/benchmark/transaction_creator.rs +++ b/sui/src/benchmark/transaction_creator.rs @@ -4,9 +4,9 @@ use crate::benchmark::validator_preparer::ValidatorPreparer; use move_core_types::{account_address::AccountAddress, ident_str}; use rayon::prelude::*; +use sui_config::NetworkConfig; use sui_types::{ base_types::*, - committee::*, crypto::{get_key_pair, AuthoritySignature, KeyPair, Signature}, messages::*, object::Object, @@ -59,12 +59,17 @@ fn create_gas_object(object_id: ObjectID, owner: SuiAddress) -> Object { } /// This builds, signs a cert -fn make_cert(keys: &[KeyPair], committee: &Committee, tx: &Transaction) -> CertifiedTransaction { +fn make_cert(network_config: &NetworkConfig, tx: &Transaction) -> CertifiedTransaction { // Make certificate let mut certificate = CertifiedTransaction::new(tx.clone()); + let committee = network_config.committee(); certificate.auth_sign_info.epoch = committee.epoch(); for i in 0..committee.quorum_threshold() { - let secx = keys.get(i).unwrap(); + let secx = network_config + .validator_configs() + .get(i) + .unwrap() + .key_pair(); let pubx = secx.public_key_bytes(); let sig = AuthoritySignature::new(&certificate.data, secx); certificate.auth_sign_info.signatures.push((*pubx, sig)); @@ -75,9 +80,8 @@ fn make_cert(keys: &[KeyPair], committee: &Committee, tx: &Transaction) -> Certi fn make_transactions( address: SuiAddress, keypair: KeyPair, - committee: &Committee, + network_config: &NetworkConfig, account_gas_objects: &[(Vec, Object)], - authority_keys: &[KeyPair], batch_size: usize, use_move: bool, ) -> Vec<(Transaction, CertifiedTransaction)> { @@ -115,7 +119,7 @@ fn make_transactions( let signature = Signature::new(&data, &keypair); let transaction = Transaction::new(data, signature); - let cert = make_cert(authority_keys, committee, &transaction); + let cert = make_cert(network_config, &transaction); (transaction, cert) }) @@ -164,8 +168,7 @@ impl TransactionCreator { tcp_conns, use_move, self.object_id_offset, - &validator_preparer.keys, - &validator_preparer.committee, + &validator_preparer.network_config, ); validator_preparer.update_objects_for_validator(txn_objects, address); @@ -219,8 +222,7 @@ impl TransactionCreator { conn: usize, use_move: bool, object_id_offset: ObjectID, - auth_keys: &[KeyPair], - committee: &Committee, + network_config: &NetworkConfig, ) -> (Vec<(Transaction, CertifiedTransaction)>, Vec) { assert_eq!(chunk_size % conn, 0); let batch_size_per_conn = chunk_size / conn; @@ -245,9 +247,8 @@ impl TransactionCreator { let transactions = make_transactions( address, key_pair, - committee, + network_config, &account_gas_objects, - auth_keys, batch_size_per_conn, use_move, ); diff --git a/sui/src/benchmark/validator_preparer.rs b/sui/src/benchmark/validator_preparer.rs index 77b5869937a06..ec31e99a03600 100644 --- a/sui/src/benchmark/validator_preparer.rs +++ b/sui/src/benchmark/validator_preparer.rs @@ -4,12 +4,11 @@ #![allow(clippy::large_enum_variant)] use crate::benchmark::bench_types::RunningMode; use crate::benchmark::load_generator::spawn_authority_server; -use crate::config::NetworkConfig; -use crate::config::{AccountConfig, Config, GenesisConfig, ObjectConfig}; +use sui_config::NetworkConfig; +use sui_config::ObjectConfig; use multiaddr::Multiaddr; use rocksdb::Options; -use std::collections::BTreeMap; use std::{ env, fs, panic, path::{Path, PathBuf}, @@ -24,57 +23,31 @@ use sui_core::authority::*; use sui_types::{ base_types::{SuiAddress, *}, committee::*, - crypto::{random_key_pairs, KeyPair, PublicKeyBytes}, + crypto::{KeyPair, PublicKeyBytes}, gas_coin::GasCoin, object::Object, }; use tokio::runtime::{Builder, Runtime}; use tracing::{error, info}; -const GENESIS_CONFIG_NAME: &str = "genesis_config.json"; - pub const VALIDATOR_BINARY_NAME: &str = "validator"; /// A helper class to set up validators for benchmarking #[allow(unused)] pub struct ValidatorPreparer { running_mode: RunningMode, - pub keys: Vec, + pub network_config: NetworkConfig, main_authority_address_hex: String, pub committee: Committee, validator_config: ValidatorConfig, } -fn set_up_authorities_and_committee( - committee_size: usize, -) -> Result<(Vec, GenesisConfig), anyhow::Error> { - let temp_dir = tempfile::tempdir()?; - let key_pairs = random_key_pairs(committee_size); - let key_pair = key_pairs[0].copy(); - let config = GenesisConfig::custom_genesis( - temp_dir.path(), - committee_size, - 0, - 0, - Some(( - key_pairs - .iter() - .map(|kp| *kp.public_key_bytes()) - .collect::>(), - key_pair, - )), - )?; - - Ok((key_pairs, config)) -} - pub enum ValidatorConfig { LocalSingleValidatorThreadConfig { authority_state: Option, authority_store: Arc, }, LocalSingleValidatorProcessConfig { - genesis_config: Option, working_dir: PathBuf, validator_process: Option, }, @@ -82,16 +55,12 @@ pub enum ValidatorConfig { } impl ValidatorPreparer { - pub fn new_for_remote( - network_config: &NetworkConfig, - keys: &BTreeMap, - ) -> Self { - let keys = keys.iter().map(|q| q.1.copy()).collect::>(); - let committee = Committee::from(network_config); + pub fn new_for_remote(network_config: NetworkConfig) -> Self { + let committee = network_config.committee(); Self { running_mode: RunningMode::RemoteValidator, - keys, + network_config, main_authority_address_hex: "".to_string(), committee, validator_config: ValidatorConfig::RemoteValidatorConfig, @@ -101,62 +70,47 @@ impl ValidatorPreparer { running_mode: RunningMode, working_dir: Option, committee_size: usize, - validator_address: Multiaddr, + _validator_address: Multiaddr, db_cpus: usize, ) -> Self { - let (keys, mut genesis_config) = set_up_authorities_and_committee(committee_size) - .expect("Got error in setting up committee"); + let temp_dir = tempfile::tempdir().unwrap(); + let network_config = NetworkConfig::generate(temp_dir.path(), committee_size); - let main_authority_address_hex = format!( - "{}", - SuiAddress::from(genesis_config.key_pair.public_key_bytes()) - ); + let main_authority_address_hex = + format!("{}", network_config.validator_configs()[0].sui_address()); info!("authority address hex: {}", main_authority_address_hex); - let committee = Committee::new( - 0, - genesis_config - .authorities - .iter() - .map(|api| (api.public_key, 1)) - .collect(), - ); + let committee = network_config.committee(); match running_mode { - RunningMode::SingleValidatorProcess => { - // Honor benchmark's host:port setting - genesis_config.authorities[0].network_address = validator_address; - Self { - running_mode, - keys, - main_authority_address_hex, - committee, - validator_config: ValidatorConfig::LocalSingleValidatorProcessConfig { - working_dir: working_dir.unwrap(), - genesis_config: Some(genesis_config), - validator_process: None, - }, - } - } + RunningMode::SingleValidatorProcess => Self { + running_mode, + network_config, + main_authority_address_hex, + committee, + validator_config: ValidatorConfig::LocalSingleValidatorProcessConfig { + working_dir: working_dir.unwrap(), + validator_process: None, + }, + }, RunningMode::SingleValidatorThread => { // Pick the first validator and create state. - let public_auth0 = keys[0].public_key_bytes(); - let secret_auth0 = keys[0].copy(); + let validator_config = &network_config.validator_configs()[0]; // Create a random directory to store the DB let path = env::temp_dir().join(format!("DB_{:?}", ObjectID::random())); let auth_state = make_authority_state( &path, db_cpus as i32, - &committee, - public_auth0, - secret_auth0, + &validator_config.committee_config().committee(), + &validator_config.public_key(), + validator_config.key_pair().copy(), ); Self { running_mode, - keys, + network_config, main_authority_address_hex, committee, validator_config: ValidatorConfig::LocalSingleValidatorThreadConfig { @@ -174,21 +128,20 @@ impl ValidatorPreparer { RunningMode::SingleValidatorProcess => { if let ValidatorConfig::LocalSingleValidatorProcessConfig { working_dir, - genesis_config, validator_process, } = &mut self.validator_config { - let config_path = working_dir.clone().join(GENESIS_CONFIG_NAME); - let config = genesis_config.take().unwrap().persisted(&config_path); - config.save().unwrap_or_else(|err| { - panic!("Can't save file {} due to {}", config_path.display(), err) - }); + let config_path = working_dir.clone().join("node.conf"); + + let config = &self.network_config.validator_configs()[0]; + + fs::write(&config_path, serde_json::to_string_pretty(&config).unwrap()) + .unwrap(); info!("Spawning a validator process..."); let child = Command::new(working_dir.clone().join(VALIDATOR_BINARY_NAME)) - .arg("--genesis-config-path") - .arg(GENESIS_CONFIG_NAME) - .arg("--force-genesis") + .arg("--network-config-path") + .arg(config_path) .spawn() .expect("failed to spawn a validator process"); validator_process.replace(child); @@ -223,10 +176,10 @@ impl ValidatorPreparer { sleep(Duration::from_secs(3)); } - pub fn update_objects_for_validator(&mut self, objects: Vec, address: SuiAddress) { + pub fn update_objects_for_validator(&mut self, objects: Vec, _address: SuiAddress) { match self.running_mode { RunningMode::SingleValidatorProcess => { - let all_objects: Vec = objects + let _all_objects: Vec = objects .iter() .map(|object| ObjectConfig { object_id: object.id(), @@ -235,19 +188,11 @@ impl ValidatorPreparer { .collect(); if let ValidatorConfig::LocalSingleValidatorProcessConfig { working_dir: _, - genesis_config, validator_process: _, } = &mut self.validator_config { - genesis_config - .as_mut() - .unwrap() - .accounts - .push(AccountConfig { - address: Some(address), - gas_objects: all_objects, - gas_object_ranges: None, - }) + //TODO objects need to be inserted at genesis time + todo!() } else { panic!("invalid validator config in local-single-validator-process mode"); } @@ -276,7 +221,6 @@ impl ValidatorPreparer { info!("Cleaning up local validator process..."); if let ValidatorConfig::LocalSingleValidatorProcessConfig { working_dir: _, - genesis_config: _, validator_process, } = &mut self.validator_config { diff --git a/sui/src/bin/bench_configure.rs b/sui/src/bin/bench_configure.rs index 804592f211e87..aa7d512c235c6 100644 --- a/sui/src/bin/bench_configure.rs +++ b/sui/src/bin/bench_configure.rs @@ -2,19 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use clap::*; -use std::collections::BTreeMap; use std::path::Path; -use sui::{ - benchmark::bench_types::RemoteLoadGenConfig, - config::{ - AccountConfig, AuthorityInfo, Config, GenesisConfig, NetworkConfig, ObjectConfigRange, - }, -}; +use sui::config::Config; +use sui_config::{AccountConfig, GenesisConfig, ObjectConfigRange}; use sui_types::{base_types::ObjectID, crypto::get_key_pair}; -const BUFFER_SIZE: usize = 650000; - #[derive(Debug, Parser)] #[clap( name = "Sui Distributed Benchmark Config Creator", @@ -34,31 +27,6 @@ pub struct DistributedBenchmarkConfigurator { fn main() { let bch = DistributedBenchmarkConfigurator::parse(); - let mut authorities = vec![]; - let mut authority_keys = BTreeMap::new(); - - // Create configs for authorities - for b in bch.host_port_stake_triplets { - let (host, port, stake) = parse_host_port_stake_triplet(&b); - let (validator_address, validator_keypair) = get_key_pair(); - let db_path = format!("DB_{}", validator_address); - let path = Path::new(&db_path); - - let auth = AuthorityInfo { - address: validator_address, - network_address: format!("/dns/{host}/tcp/{port}/http").parse().unwrap(), - db_path: path.to_path_buf(), - stake, - consensus_address: format!("/dns/{host}/tcp/{}/http", port + 1) - .parse() - .unwrap(), - public_key: *validator_keypair.public_key_bytes(), - }; - - authorities.push(auth); - authority_keys.insert(*validator_keypair.public_key_bytes(), validator_keypair); - } - // For each load generator, create an address as the source of transfers, and configs let mut accounts = vec![]; let mut obj_id_offset = bch.object_id_offset; @@ -89,58 +57,21 @@ fn main() { }; accounts.push(account); } - - // For each validator, fill in the account info into genesis - for (i, (_, (_, kp))) in authorities.iter().zip(authority_keys.iter()).enumerate() { - // Create and save the genesis configs for the validators - let genesis_config = GenesisConfig { - authorities: authorities.clone(), - accounts: accounts.clone(), - move_packages: vec![], - sui_framework_lib_path: Path::new("../../sui_programmability/framework").to_path_buf(), - move_framework_lib_path: Path::new( - "../../sui_programmability/framework/deps/move-stdlib", - ) + let genesis_config = GenesisConfig { + committee_size: bch.host_port_stake_triplets.len(), + accounts: accounts.clone(), + move_packages: vec![], + sui_framework_lib_path: Path::new("../../sui_programmability/framework").to_path_buf(), + move_framework_lib_path: Path::new("../../sui_programmability/framework/deps/move-stdlib") .to_path_buf(), - key_pair: kp.copy(), - }; - let path_str = format!("distributed_bench_genesis_{}.conf", i); - let genesis_path = Path::new(&path_str); - genesis_config.persisted(genesis_path).save().unwrap(); - } - - // For each load gen, provide configs and kps - for (i, (_account_cfg, (account_keypair, object_id_offset))) in accounts - .iter() - .zip(account_private_info) - .into_iter() - .enumerate() - { - // Create and save network configs - let network_config = NetworkConfig { - epoch: 0, - authorities: authorities.clone(), - buffer_size: BUFFER_SIZE, - loaded_move_packages: vec![], - // This keypair is not important in this field - key_pair: authority_keys.iter().last().unwrap().1.copy(), - }; - - // Create and save load gen configs - let lg = RemoteLoadGenConfig { - account_keypair, - object_id_offset, - validator_keypairs: authority_keys.iter().map(|q| (*q.0, q.1.copy())).collect(), - network_config, - }; - let path_str = format!("load_gen_{}.conf", i); - let load_gen_path = Path::new(&path_str); + }; - lg.persisted(load_gen_path).save().unwrap(); - } + let path_str = "distributed_bench_genesis.conf"; + let genesis_path = Path::new(&path_str); + genesis_config.persisted(genesis_path).save().unwrap(); } -fn parse_host_port_stake_triplet(s: &str) -> (String, u16, usize) { +fn _parse_host_port_stake_triplet(s: &str) -> (String, u16, usize) { let tokens: Vec = s.split(':').into_iter().map(|t| t.to_owned()).collect(); assert_eq!(tokens.len(), 3); diff --git a/sui/src/bin/remote_load_generator.rs b/sui/src/bin/remote_load_generator.rs index 0e3d01b6b19fd..4196f9d312930 100644 --- a/sui/src/bin/remote_load_generator.rs +++ b/sui/src/bin/remote_load_generator.rs @@ -10,7 +10,8 @@ use std::panic; use std::path::PathBuf; use sui::benchmark::transaction_creator::TransactionCreator; use sui::benchmark::validator_preparer::ValidatorPreparer; -use sui::config::{NetworkConfig, PersistedConfig}; +use sui::config::PersistedConfig; +use sui_config::NetworkConfig; use sui_types::base_types::ObjectID; use sui_types::crypto::KeyPair; use tokio::runtime::Builder; @@ -61,8 +62,11 @@ pub fn main() { let network_config: NetworkConfig = remote_config.network_config; - let validator_preparer = - ValidatorPreparer::new_for_remote(&network_config, &remote_config.validator_keypairs); + let validator_preparer = ValidatorPreparer::new_for_remote(network_config); + let remote_config: RemoteLoadGenConfig = + PersistedConfig::read(&benchmark.remote_config).unwrap(); + + let network_config: NetworkConfig = remote_config.network_config; let connections = if benchmark.tcp_connections > 0 { benchmark.tcp_connections } else { diff --git a/sui/src/bin/validator.rs b/sui/src/bin/validator.rs index 217262234c1f3..458b0bc636a65 100644 --- a/sui/src/bin/validator.rs +++ b/sui/src/bin/validator.rs @@ -1,22 +1,15 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::anyhow; use clap::*; use multiaddr::Multiaddr; -use narwhal_config::Parameters as ConsensusParameters; use std::path::PathBuf; use sui::{ - config::{ - sui_config_dir, GenesisConfig, NetworkConfig, PersistedConfig, CONSENSUS_DB_NAME, - SUI_NETWORK_CONFIG, - }, + config::{sui_config_dir, SUI_NETWORK_CONFIG}, sui_commands::{genesis, make_server}, }; -use sui_types::{ - base_types::{encode_bytes_hex, SuiAddress}, - committee::Committee, -}; +use sui_config::PersistedConfig; +use sui_config::{GenesisConfig, ValidatorConfig}; use tracing::{error, info}; const PROM_PORT_ADDR: &str = "127.0.0.1:9184"; @@ -57,47 +50,22 @@ async fn main() -> Result<(), anyhow::Error> { let network_config_path = sui_config_dir()?.join(SUI_NETWORK_CONFIG); - let network_config = match (network_config_path.exists(), cfg.force_genesis) { - (true, false) => PersistedConfig::::read(&network_config_path)?, + let validator_config = match (network_config_path.exists(), cfg.force_genesis) { + (true, false) => PersistedConfig::::read(&network_config_path)?, // If network.conf is missing, or if --force-genesis is true, we run genesis. _ => { - let genesis_conf: GenesisConfig = PersistedConfig::read(&cfg.genesis_config_path)?; - let adddress = SuiAddress::from(genesis_conf.key_pair.public_key_bytes()); - let (network_config, _, _) = genesis(genesis_conf, Some(adddress)).await?; - network_config + let mut genesis_conf: GenesisConfig = PersistedConfig::read(&cfg.genesis_config_path)?; + genesis_conf.committee_size = 1; + let (network_config, _, _) = genesis(genesis_conf).await?; + network_config.into_validator_configs().remove(0) } }; - let public_key_bytes = network_config.key_pair.public_key_bytes(); - let address = SuiAddress::from(public_key_bytes); - // Find the network config for this validator - let authority = network_config - .authorities - .iter() - .find(|x| SuiAddress::from(&x.public_key) == address) - .ok_or_else(|| { - anyhow!( - "Keypair (pub key: {:?}) in network config is not in the validator committee", - public_key_bytes, - ) - })?; - let listen_address = cfg .listen_address - .unwrap_or_else(|| authority.network_address.clone()); - - let consensus_committee = network_config.make_narwhal_committee(); - - let consensus_parameters = ConsensusParameters { - max_header_delay: std::time::Duration::from_millis(5_000), - max_batch_delay: std::time::Duration::from_millis(5_000), - ..ConsensusParameters::default() - }; - let consensus_store_path = sui_config_dir()? - .join(CONSENSUS_DB_NAME) - .join(encode_bytes_hex(&authority.public_key)); + .unwrap_or_else(|| validator_config.network_address().to_owned()); - info!(authority =? authority.public_key, public_addr =? authority.network_address, + info!(validator =? validator_config.public_key(), public_addr =? validator_config.network_address(), "Initializing authority listening on {}", listen_address ); @@ -107,22 +75,13 @@ async fn main() -> Result<(), anyhow::Error> { prometheus_exporter::start(prom_binding).expect("Failed to start Prometheus exporter"); // Pass in the newtwork parameters of all authorities - let net = network_config.get_authority_infos(); - if let Err(e) = make_server( - authority, - &network_config.key_pair, - &Committee::from(&network_config), - &consensus_committee, - &consensus_store_path, - &consensus_parameters, - Some(net), - ) - .await? - .spawn_with_bind_address(listen_address) - .await - .unwrap() - .join() - .await + if let Err(e) = make_server(&validator_config) + .await? + .spawn_with_bind_address(listen_address) + .await + .unwrap() + .join() + .await { error!("Validator server ended with an error: {e}"); } diff --git a/sui/src/config/gateway.rs b/sui/src/config/gateway.rs index a7b0db30a61f5..d3760989debb2 100644 --- a/sui/src/config/gateway.rs +++ b/sui/src/config/gateway.rs @@ -1,10 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{ - config::{AuthorityInfo, Config}, - rpc_gateway_client::RpcGatewayClient, -}; +use crate::{config::Config, rpc_gateway_client::RpcGatewayClient}; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, @@ -12,6 +9,7 @@ use std::{ path::PathBuf, time::Duration, }; +use sui_config::ValidatorInfo; use sui_core::{ authority_client::NetworkAuthorityClient, gateway_state::{GatewayClient, GatewayState}, @@ -40,7 +38,10 @@ impl Display for GatewayType { "Gateway state DB folder path : {:?}", config.db_folder_path )?; - let authorities = config.authorities.iter().map(|info| &info.network_address); + let authorities = config + .validator_set + .iter() + .map(|info| info.network_address()); writeln!( writer, "Authorities : {:?}", @@ -73,7 +74,7 @@ impl GatewayType { #[derive(Serialize, Deserialize)] pub struct GatewayConfig { pub epoch: EpochId, - pub authorities: Vec, + pub validator_set: Vec, pub send_timeout: Duration, pub recv_timeout: Duration, pub buffer_size: usize, @@ -85,9 +86,9 @@ impl Config for GatewayConfig {} impl GatewayConfig { pub fn make_committee(&self) -> Committee { let voting_rights = self - .authorities + .validator_set .iter() - .map(|authority| (authority.public_key, 1)) + .map(|validator| (validator.public_key(), validator.stake())) .collect(); Committee::new(self.epoch, voting_rights) } @@ -97,10 +98,10 @@ impl GatewayConfig { let mut config = mysten_network::config::Config::new(); config.connect_timeout = Some(self.send_timeout); config.request_timeout = Some(self.recv_timeout); - for authority in &self.authorities { - let channel = config.connect_lazy(&authority.network_address).unwrap(); + for authority in &self.validator_set { + let channel = config.connect_lazy(authority.network_address()).unwrap(); let client = NetworkAuthorityClient::new(channel); - authority_clients.insert(authority.public_key, client); + authority_clients.insert(authority.public_key(), client); } authority_clients } @@ -110,7 +111,7 @@ impl Default for GatewayConfig { fn default() -> Self { Self { epoch: 0, - authorities: vec![], + validator_set: vec![], send_timeout: Duration::from_micros(4000000), recv_timeout: Duration::from_micros(4000000), buffer_size: 650000, diff --git a/sui/src/config/mod.rs b/sui/src/config/mod.rs index f7f1112236504..80e5bb51f37ee 100644 --- a/sui/src/config/mod.rs +++ b/sui/src/config/mod.rs @@ -4,31 +4,21 @@ use crate::keystore::KeystoreType; use anyhow::bail; -use multiaddr::{Multiaddr, Protocol}; -use narwhal_config::{ - Authority, Committee as ConsensusCommittee, PrimaryAddresses, Stake, WorkerAddresses, -}; -use narwhal_crypto::ed25519::Ed25519PublicKey; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; +use serde::{Deserialize, Serialize}; use serde_with::{hex::Hex, serde_as}; use std::{ fmt::{Display, Formatter, Write}, - fs::{self, create_dir_all, File}, - io::BufReader, - ops::{Deref, DerefMut}, - path::{Path, PathBuf}, -}; -use sui_framework::DEFAULT_FRAMEWORK_PATH; -use sui_types::{ - base_types::*, - committee::{Committee, EpochId}, - crypto::{get_key_pair, KeyPair, PublicKeyBytes}, + fs::create_dir_all, + path::PathBuf, }; -use tracing::log::trace; +use sui_types::base_types::*; + +pub use sui_config::Config; +pub use sui_config::PersistedConfig; pub mod gateway; -pub mod utils; + +pub use sui_config::utils; pub use gateway::{GatewayConfig, GatewayType}; @@ -55,76 +45,10 @@ pub fn sui_config_dir() -> Result { }) } -const DEFAULT_WEIGHT: usize = 1; -const DEFAULT_GAS_AMOUNT: u64 = 100000; pub const AUTHORITIES_DB_NAME: &str = "authorities_db"; pub const DEFAULT_STARTING_PORT: u16 = 10000; pub const CONSENSUS_DB_NAME: &str = "consensus_db"; -#[derive(Serialize, Debug, Clone)] -pub struct AuthorityInfo { - pub address: SuiAddress, - pub public_key: PublicKeyBytes, - pub network_address: Multiaddr, - pub db_path: PathBuf, - pub stake: usize, - pub consensus_address: Multiaddr, -} - -type AuthorityKeys = (Vec, KeyPair); - -// Custom deserializer with optional default fields -impl<'de> Deserialize<'de> for AuthorityInfo { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let (_, new_key_pair) = get_key_pair(); - - let json = Value::deserialize(deserializer)?; - let public_key_bytes = if let Some(val) = json.get("public_key") { - PublicKeyBytes::deserialize(val).map_err(serde::de::Error::custom)? - } else { - *new_key_pair.public_key_bytes() - }; - let network_address = if let Some(val) = json.get("network_address") { - Multiaddr::deserialize(val).map_err(serde::de::Error::custom)? - } else { - format!("/ip4/127.0.0.1/tcp/{}/http", utils::get_available_port()) - .parse() - .unwrap() - }; - let db_path = if let Some(val) = json.get("db_path") { - PathBuf::deserialize(val).map_err(serde::de::Error::custom)? - } else { - PathBuf::from(".") - .join(AUTHORITIES_DB_NAME) - .join(encode_bytes_hex(&public_key_bytes)) - }; - let stake = if let Some(val) = json.get("stake") { - usize::deserialize(val).map_err(serde::de::Error::custom)? - } else { - DEFAULT_WEIGHT - }; - let consensus_address = if let Some(val) = json.get("consensus_address") { - Multiaddr::deserialize(val).map_err(serde::de::Error::custom)? - } else { - format!("/ip4/127.0.0.1/tcp/{}/http", utils::get_available_port()) - .parse() - .unwrap() - }; - - Ok(AuthorityInfo { - address: SuiAddress::from(&public_key_bytes), - public_key: public_key_bytes, - network_address, - db_path, - stake, - consensus_address, - }) - } -} - #[serde_as] #[derive(Serialize, Deserialize)] pub struct WalletConfig { @@ -153,347 +77,3 @@ impl Display for WalletConfig { write!(f, "{}", writer) } } - -#[derive(Serialize, Deserialize)] -pub struct NetworkConfig { - pub epoch: EpochId, - pub authorities: Vec, - pub buffer_size: usize, - pub loaded_move_packages: Vec<(PathBuf, ObjectID)>, - pub key_pair: KeyPair, -} - -impl Config for NetworkConfig {} - -impl NetworkConfig { - pub fn get_authority_infos(&self) -> Vec { - self.authorities.clone() - } - - pub fn make_narwhal_committee(&self) -> ConsensusCommittee { - ConsensusCommittee { - authorities: self - .authorities - .iter() - .map(|x| { - let name = x - .public_key - .make_narwhal_public_key() - .expect("Can't get narwhal public key"); - let primary = PrimaryAddresses { - primary_to_primary: update_tcp_port_in_multiaddr(&x.network_address, 100), - worker_to_primary: update_tcp_port_in_multiaddr(&x.network_address, 200), - }; - let workers = [( - /* worker_id */ 0, - WorkerAddresses { - primary_to_worker: update_tcp_port_in_multiaddr( - &x.network_address, - 300, - ), - transactions: x.consensus_address.clone(), - worker_to_worker: update_tcp_port_in_multiaddr(&x.network_address, 400), - }, - )] - .iter() - .cloned() - .collect(); - let authority = Authority { - stake: x.stake as Stake, - primary, - workers, - }; - (name, authority) - }) - .collect(), - } - } -} - -impl From<&NetworkConfig> for Committee { - fn from(network_config: &NetworkConfig) -> Committee { - let voting_rights = network_config - .authorities - .iter() - .map(|authority| (authority.public_key, authority.stake)) - .collect(); - Committee::new(network_config.epoch, voting_rights) - } -} - -#[derive(Serialize, Deserialize)] -#[serde(default)] -pub struct GenesisConfig { - pub authorities: Vec, - pub accounts: Vec, - pub move_packages: Vec, - pub sui_framework_lib_path: PathBuf, - pub move_framework_lib_path: PathBuf, - pub key_pair: KeyPair, -} - -impl Config for GenesisConfig {} - -#[derive(Serialize, Deserialize, Default, Debug, Clone)] -#[serde(default)] -pub struct AccountConfig { - #[serde( - skip_serializing_if = "Option::is_none", - serialize_with = "SuiAddress::optional_address_as_hex", - deserialize_with = "SuiAddress::optional_address_from_hex" - )] - pub address: Option, - pub gas_objects: Vec, - pub gas_object_ranges: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ObjectConfigRange { - /// Starting object id - pub offset: ObjectID, - /// Number of object ids - pub count: u64, - /// Gas value per object id - pub gas_value: u64, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ObjectConfig { - #[serde(default = "ObjectID::random")] - pub object_id: ObjectID, - #[serde(default = "default_gas_value")] - pub gas_value: u64, -} - -fn default_gas_value() -> u64 { - DEFAULT_GAS_AMOUNT -} - -const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4; -const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5; -const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5; - -impl GenesisConfig { - pub fn default_genesis( - working_dir: &Path, - authority_keys: Option, - ) -> Result { - let num_authorities = match &authority_keys { - Some((public_keys, _)) => public_keys.len(), - None => DEFAULT_NUMBER_OF_AUTHORITIES, - }; - - GenesisConfig::custom_genesis( - working_dir, - num_authorities, - DEFAULT_NUMBER_OF_ACCOUNT, - DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT, - authority_keys, - ) - } - - pub fn custom_genesis( - working_dir: &Path, - num_authorities: usize, - num_accounts: usize, - num_objects_per_account: usize, - authority_keys: Option, - ) -> Result { - assert!( - num_authorities > 0, - "num_authorities should be larger than 0" - ); - let mut authorities = Vec::with_capacity(num_authorities); - for _ in 0..num_authorities { - // Get default authority config from deserialization logic. - let mut authority = AuthorityInfo::deserialize(Value::String(String::new()))?; - authority.db_path = working_dir - .join(AUTHORITIES_DB_NAME) - .join(encode_bytes_hex(&authority.public_key)); - authorities.push(authority) - } - let authority_key_pair; - if let Some((public_keys, keypair)) = authority_keys { - // Use key pairs if given - assert_eq!( - public_keys.len(), - num_authorities, - "Number of key pairs does not maych num_authorities" - ); - public_keys - .iter() - .find(|pk| pk == &keypair.public_key_bytes()) - .expect("Keypair should be part of thte committee"); - authority_key_pair = keypair; - for i in 0..num_authorities { - authorities[i].public_key = public_keys[i]; - authorities[i].address = SuiAddress::from(&public_keys[i]); - } - } else { - let (address, key_pair) = get_key_pair(); - // If authorities is not empty, we override the first one - if !authorities.is_empty() { - authorities[0].address = address; - authorities[0].public_key = *key_pair.public_key_bytes(); - } - authority_key_pair = key_pair; - } - let mut accounts = Vec::new(); - for _ in 0..num_accounts { - let mut objects = Vec::new(); - for _ in 0..num_objects_per_account { - objects.push(ObjectConfig { - object_id: ObjectID::random(), - gas_value: DEFAULT_GAS_AMOUNT, - }) - } - accounts.push(AccountConfig { - address: None, - gas_objects: objects, - gas_object_ranges: Some(Vec::new()), - }) - } - - Ok(Self { - authorities, - accounts, - key_pair: authority_key_pair, - ..Default::default() - }) - } -} - -impl Default for GenesisConfig { - fn default() -> Self { - Self { - authorities: vec![], - accounts: vec![], - move_packages: vec![], - sui_framework_lib_path: PathBuf::from(DEFAULT_FRAMEWORK_PATH), - move_framework_lib_path: PathBuf::from(DEFAULT_FRAMEWORK_PATH) - .join("deps") - .join("move-stdlib"), - key_pair: get_key_pair().1, - } - } -} - -pub trait Config -where - Self: DeserializeOwned + Serialize, -{ - fn persisted(self, path: &Path) -> PersistedConfig { - PersistedConfig { - inner: self, - path: path.to_path_buf(), - } - } -} - -pub struct PersistedConfig { - inner: C, - path: PathBuf, -} - -impl PersistedConfig -where - C: Config, -{ - pub fn read(path: &Path) -> Result { - trace!("Reading config from '{:?}'", path); - let reader = BufReader::new(File::open(path)?); - Ok(serde_json::from_reader(reader)?) - } - - pub fn save(&self) -> Result<(), anyhow::Error> { - trace!("Writing config to '{:?}'", &self.path); - let config = serde_json::to_string_pretty(&self.inner)?; - fs::write(&self.path, config)?; - Ok(()) - } -} - -impl Deref for PersistedConfig { - type Target = C; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl DerefMut for PersistedConfig { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -/// Make a default Narwhal-compatible committee. -pub fn make_default_narwhal_committee( - authorities: &[AuthorityInfo], -) -> Result, anyhow::Error> { - let mut ports = Vec::new(); - for _ in authorities { - let authority_ports = [ - utils::get_available_port(), - utils::get_available_port(), - utils::get_available_port(), - utils::get_available_port(), - ]; - ports.push(authority_ports); - } - - Ok(ConsensusCommittee { - authorities: authorities - .iter() - .enumerate() - .map(|(i, x)| { - let name = x - .public_key - .make_narwhal_public_key() - .expect("Can't get narwhal public key"); - - let primary = PrimaryAddresses { - primary_to_primary: format!("/ip4/127.0.0.1/tcp/{}/http", ports[i][0]) - .parse() - .unwrap(), - worker_to_primary: format!("/ip4/127.0.0.1/tcp/{}/http", ports[i][1]) - .parse() - .unwrap(), - }; - let workers = [( - /* worker_id */ 0, - WorkerAddresses { - primary_to_worker: format!("/ip4/127.0.0.1/tcp/{}/http", ports[i][2]) - .parse() - .unwrap(), - transactions: x.consensus_address.clone(), - worker_to_worker: format!("/ip4/127.0.0.1/tcp/{}/http", ports[i][3]) - .parse() - .unwrap(), - }, - )] - .iter() - .cloned() - .collect(); - - let authority = Authority { - stake: x.stake as Stake, - primary, - workers, - }; - (name, authority) - }) - .collect(), - }) -} - -fn update_tcp_port_in_multiaddr(addr: &Multiaddr, offset: u16) -> Multiaddr { - addr.replace(1, |protocol| { - if let Protocol::Tcp(port) = protocol { - Some(Protocol::Tcp(port + offset)) - } else { - panic!("expected tcp protocol at index 1"); - } - }) - .expect("tcp protocol at index 1") -} diff --git a/sui/src/sui_commands.rs b/sui/src/sui_commands.rs index 2202849317fdb..95d54195030ef 100644 --- a/sui/src/sui_commands.rs +++ b/sui/src/sui_commands.rs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ config::{ - make_default_narwhal_committee, sui_config_dir, AuthorityInfo, Config, GatewayConfig, - GatewayType, GenesisConfig, NetworkConfig, PersistedConfig, WalletConfig, - CONSENSUS_DB_NAME, SUI_GATEWAY_CONFIG, SUI_NETWORK_CONFIG, SUI_WALLET_CONFIG, + sui_config_dir, Config, GatewayConfig, GatewayType, PersistedConfig, WalletConfig, + SUI_GATEWAY_CONFIG, SUI_NETWORK_CONFIG, SUI_WALLET_CONFIG, }, keystore::{Keystore, KeystoreType, SuiKeystore}, }; @@ -14,33 +13,26 @@ use clap::*; use futures::future::join_all; use move_binary_format::CompiledModule; use move_package::BuildConfig; -use narwhal_config::{Committee as ConsensusCommittee, Parameters as ConsensusParameters}; -use narwhal_crypto::ed25519::Ed25519PublicKey; - use std::collections::BTreeMap; use std::collections::BTreeSet; -use std::collections::HashMap; use std::fs; -use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; use sui_adapter::adapter::generate_package_id; use sui_adapter::genesis; +use sui_config::NetworkConfig; +use sui_config::{GenesisConfig, ValidatorConfig}; use sui_core::authority::{AuthorityState, AuthorityStore}; use sui_core::authority_active::ActiveAuthority; use sui_core::authority_client::NetworkAuthorityClient; use sui_core::authority_server::AuthorityServer; use sui_core::authority_server::AuthorityServerHandle; use sui_core::consensus_adapter::ConsensusListener; -use sui_types::base_types::encode_bytes_hex; use sui_types::base_types::{decode_bytes_hex, ObjectID}; use sui_types::base_types::{SequenceNumber, SuiAddress, TxContext}; -use sui_types::committee::Committee; -use sui_types::crypto::{random_key_pairs, KeyPair}; use sui_types::error::SuiResult; use sui_types::object::Object; - use tokio::sync::mpsc::channel; use tracing::{error, info}; @@ -103,18 +95,8 @@ impl SuiCommand { )) })?; - let authority_key_path = config - .clone() - .unwrap_or(sui_config_dir()?.join(SUI_AUTHORITY_KEYS)); - assert!( - authority_key_path.exists(), - "{:?} does not exist, you may want to re-genesis from scratch", - authority_key_path - ); - let authority_keys = SuiKeystore::load_or_create(&authority_key_path)?; - // Start a sui validator (including its consensus node). - SuiNetwork::start(&network_config, authority_keys.key_pairs()) + SuiNetwork::start(&network_config) .await? .wait_for_completion() .await @@ -134,9 +116,12 @@ impl SuiCommand { })?; if *dump_addresses { - for auth in config.authorities.iter() { - let addr = SuiAddress::from(&auth.public_key); - println!("{} - {}", auth.network_address, addr); + for validator in config.validator_configs() { + println!( + "{} - {}", + validator.network_address(), + validator.sui_address() + ); } } Ok(()) @@ -201,7 +186,7 @@ impl SuiCommand { let genesis_conf = match from_config { Some(q) => PersistedConfig::read(q)?, - None => create_genesis_config_from_scratch(sui_config_dir)?, + None => GenesisConfig::for_local_testing()?, }; if let Some(path) = write_config { @@ -210,7 +195,7 @@ impl SuiCommand { return Ok(()); } - let (network_config, accounts, mut keystore) = genesis(genesis_conf, None).await?; + let (network_config, accounts, mut keystore) = genesis(genesis_conf).await?; info!("Network genesis completed."); let network_config = network_config.persisted(&network_path); network_config.save()?; @@ -222,9 +207,13 @@ impl SuiCommand { // Use the first address if any let active_address = accounts.get(0).copied(); + let validator_set = network_config.validator_configs()[0] + .committee_config() + .validator_set(); + GatewayConfig { db_folder_path: gateway_db_folder_path, - authorities: network_config.get_authority_infos(), + validator_set: validator_set.to_owned(), ..Default::default() } .persisted(&gateway_path) @@ -233,7 +222,7 @@ impl SuiCommand { let wallet_gateway_config = GatewayConfig { db_folder_path, - authorities: network_config.get_authority_infos(), + validator_set: validator_set.to_owned(), ..Default::default() }; @@ -285,69 +274,21 @@ pub struct SuiNetwork { } impl SuiNetwork { - pub async fn start( - config: &NetworkConfig, - key_pairs: Vec<&KeyPair>, - ) -> Result { - if config.authorities.is_empty() { + pub async fn start(config: &NetworkConfig) -> Result { + if config.validator_configs().is_empty() { return Err(anyhow!( "No authority configured for the network, please run genesis." )); } - if config.authorities.len() != key_pairs.len() { - return Err(anyhow!( - "Num of authorities does not match num of key_pairs." - )); - } - - let key_pairs = key_pairs - .iter() - .map(|kp| (kp.public_key_bytes(), kp)) - .collect::>(); info!( "Starting network with {} authorities", - config.authorities.len() - ); - - let committee = Committee::new( - config.epoch, - config - .authorities - .iter() - .map(|info| (info.public_key, info.stake)) - .collect(), + config.validator_configs().len() ); - let consensus_committee = make_default_narwhal_committee(&config.authorities)?; - - // Pass in the newtwork parameters of all authorities - let net = config.get_authority_infos(); - let mut spawned_authorities = Vec::new(); - - for authority in &config.authorities { - let consensus_parameters = ConsensusParameters::default(); - let key_pair = key_pairs.get(&authority.public_key).unwrap_or_else(|| { - panic!( - "Can't find key pair for authority {:?}", - &authority.public_key - ) - }); - let consensus_store_path = sui_config_dir()? - .join(CONSENSUS_DB_NAME) - .join(encode_bytes_hex(&authority.public_key)); - - let server = make_server( - authority, - key_pair, - &committee, - &consensus_committee, - &consensus_store_path, - &consensus_parameters, - Some(net.clone()), - ) - .await?; + for validator in config.validator_configs() { + let server = make_server(validator).await?; spawned_authorities.push(server.spawn().await?); } info!("Started {} authorities", spawned_authorities.len()); @@ -381,28 +322,13 @@ impl SuiNetwork { pub async fn genesis( genesis_conf: GenesisConfig, - single_address: Option, ) -> Result<(NetworkConfig, Vec, SuiKeystore), anyhow::Error> { - let num_to_provision = if single_address.is_none() { - genesis_conf.authorities.len() - } else { - 1 - }; + let num_to_provision = genesis_conf.committee_size; info!("Creating {} new authorities...", num_to_provision); - let mut network_config = NetworkConfig { - epoch: 0, - authorities: vec![], - buffer_size: 650000, - loaded_move_packages: vec![], - key_pair: genesis_conf.key_pair, - }; - let mut voting_right = BTreeMap::new(); - for authority in genesis_conf.authorities { - voting_right.insert(authority.public_key, authority.stake); - network_config.authorities.push(authority); - } + let config_dir = sui_config_dir().unwrap(); + let mut network_config = NetworkConfig::generate(&config_dir, num_to_provision); let mut addresses = Vec::new(); let mut preload_modules: Vec> = Vec::new(); @@ -493,23 +419,14 @@ pub async fn genesis( info!("Loaded package [{}] from {:?}.", package_id, path); // Writing package id to network config for user to retrieve later. - network_config.loaded_move_packages.push((path, package_id)); + network_config.add_move_package(path, package_id); preload_modules.push(modules) } } - let committee = Committee::new(network_config.epoch, voting_right); - for authority in &network_config.authorities { - if let Some(addr) = single_address { - if addr != authority.address { - continue; - } - } - + for validator in network_config.validator_configs() { make_server_with_genesis_ctx( - authority, - &network_config.key_pair, - &committee, + validator, preload_modules.clone(), &preload_objects, &mut genesis_ctx.clone(), @@ -520,52 +437,33 @@ pub async fn genesis( Ok((network_config, addresses, keystore)) } -pub async fn make_server( - authority: &AuthorityInfo, - key_pair: &KeyPair, - committee: &Committee, - consensus_committee: &ConsensusCommittee, - consensus_store_path: &Path, - consensus_parameters: &ConsensusParameters, - net_parameters: Option>, -) -> SuiResult { - let store = Arc::new(AuthorityStore::open(&authority.db_path, None)); - let name = authority.public_key; +pub async fn make_server(validator_config: &ValidatorConfig) -> SuiResult { + let store = Arc::new(AuthorityStore::open(validator_config.db_path(), None)); + let name = validator_config.public_key(); let state = AuthorityState::new_without_genesis( - committee.clone(), + validator_config.committee_config().committee(), name, - Arc::pin(key_pair.copy()), + Arc::pin(validator_config.key_pair().copy()), store, ) .await; - make_authority( - authority, - key_pair, - state, - consensus_committee, - consensus_store_path, - consensus_parameters, - net_parameters, - ) - .await + make_authority(validator_config, state).await } async fn make_server_with_genesis_ctx( - authority: &AuthorityInfo, - key_pair: &KeyPair, - committee: &Committee, + validator_config: &ValidatorConfig, preload_modules: Vec>, preload_objects: &[Object], genesis_ctx: &mut TxContext, ) -> SuiResult { - let store = Arc::new(AuthorityStore::open(&authority.db_path, None)); - let name = authority.public_key; + let store = Arc::new(AuthorityStore::open(validator_config.db_path(), None)); + let name = *validator_config.key_pair().public_key_bytes(); let state = AuthorityState::new( - committee.clone(), + validator_config.committee_config().committee(), name, - Arc::pin(key_pair.copy()), + Arc::pin(validator_config.key_pair().copy()), store, preload_modules, genesis_ctx, @@ -579,9 +477,9 @@ async fn make_server_with_genesis_ctx( let (tx_sui_to_consensus, _rx_sui_to_consensus) = channel(1); Ok(AuthorityServer::new( - authority.network_address.clone(), + validator_config.network_address().clone(), Arc::new(state), - authority.consensus_address.clone(), + validator_config.consensus_config().address().clone(), /* tx_consensus_listener */ tx_sui_to_consensus, )) } @@ -589,13 +487,8 @@ async fn make_server_with_genesis_ctx( /// Spawn all the subsystems run by a Sui authority: a consensus node, a sui authority server, /// and a consensus listener bridging the consensus node and the sui authority. pub async fn make_authority( - authority: &AuthorityInfo, - key_pair: &KeyPair, + validator_config: &ValidatorConfig, state: AuthorityState, - consensus_committee: &ConsensusCommittee, - consensus_store_path: &Path, - consensus_parameters: &ConsensusParameters, - net_parameters: Option>, ) -> SuiResult { let (tx_consensus_to_sui, rx_consensus_to_sui) = channel(1_000); let (tx_sui_to_consensus, rx_sui_to_consensus) = channel(1_000); @@ -603,14 +496,21 @@ pub async fn make_authority( let authority_state = Arc::new(state); // Spawn the consensus node of this authority. - let consensus_keypair = key_pair.make_narwhal_keypair(); + let consensus_keypair = validator_config.key_pair().make_narwhal_keypair(); let consensus_name = consensus_keypair.name.clone(); - let consensus_store = narwhal_node::NodeStorage::reopen(consensus_store_path); + let consensus_store = + narwhal_node::NodeStorage::reopen(validator_config.consensus_config().db_path()); narwhal_node::Node::spawn_primary( consensus_keypair, - consensus_committee.clone(), + validator_config + .committee_config() + .narwhal_committee() + .to_owned(), &consensus_store, - consensus_parameters.clone(), + validator_config + .consensus_config() + .narwhal_config() + .to_owned(), /* consensus */ true, // Indicate that we want to run consensus. /* execution_state */ authority_state.clone(), /* tx_confirmation */ tx_consensus_to_sui, @@ -619,9 +519,15 @@ pub async fn make_authority( narwhal_node::Node::spawn_workers( consensus_name, /* ids */ vec![0], // We run a single worker with id '0'. - consensus_committee.clone(), + validator_config + .committee_config() + .narwhal_committee() + .to_owned(), &consensus_store, - consensus_parameters.clone(), + validator_config + .consensus_config() + .narwhal_config() + .to_owned(), ); // Spawn a consensus listener. It listen for consensus outputs and notifies the @@ -634,15 +540,15 @@ pub async fn make_authority( // If we have network information make authority clients // to all authorities in the system. - let _active_authority: Option<()> = if let Some(network) = net_parameters { + let _active_authority: Option<()> = { let mut authority_clients = BTreeMap::new(); let mut config = mysten_network::config::Config::new(); config.connect_timeout = Some(Duration::from_secs(5)); config.request_timeout = Some(Duration::from_secs(5)); - for info in &network { - let channel = config.connect_lazy(&info.network_address).unwrap(); + for validator in validator_config.committee_config().validator_set() { + let channel = config.connect_lazy(validator.network_address()).unwrap(); let client = NetworkAuthorityClient::new(channel); - authority_clients.insert(info.public_key, client); + authority_clients.insert(validator.public_key(), client); } let _active_authority = ActiveAuthority::new(authority_state.clone(), authority_clients)?; @@ -652,42 +558,13 @@ pub async fn make_authority( // let join_handle = active_authority.spawn_all_active_processes().await; // Some(join_handle) None - } else { - None }; // Return new authority server. It listen to users transactions and send back replies. Ok(AuthorityServer::new( - authority.network_address.clone(), + validator_config.network_address().to_owned(), authority_state, - authority.consensus_address.clone(), + validator_config.consensus_config().address().to_owned(), /* tx_consensus_listener */ tx_sui_to_consensus, )) } - -/// Generate a genesis config -/// Side effect: create an authorities.key file that contains all authority key pairs. -/// the file is only for local testing's convenience, and not supposed to -/// exist in testnet/mainnet. -fn create_genesis_config_from_scratch( - sui_config_dir: &Path, -) -> Result { - let authority_key_pairs_path = sui_config_dir.join(SUI_AUTHORITY_KEYS); - let key_pairs = random_key_pairs(4); - let mut authority_key_store = SuiKeystore::default(); - authority_key_store.set_path(&authority_key_pairs_path); - let key_pair = key_pairs[0].copy(); - let public_keys = key_pairs - .iter() - .map(|kp| *kp.public_key_bytes()) - .collect::>(); - for key_pair in key_pairs { - authority_key_store.add_key(SuiAddress::from(key_pair.public_key_bytes()), key_pair)?; - } - authority_key_store.save()?; - info!( - "Authority keystore is stored in {:?}.", - authority_key_pairs_path - ); - GenesisConfig::default_genesis(sui_config_dir, Some((public_keys, key_pair))) -} diff --git a/sui/src/unit_tests/cli_tests.rs b/sui/src/unit_tests/cli_tests.rs index 71c0a9f7e3056..0ad9a0cbc13c0 100644 --- a/sui/src/unit_tests/cli_tests.rs +++ b/sui/src/unit_tests/cli_tests.rs @@ -12,25 +12,25 @@ use tracing_test::traced_test; use sui::{ config::{ - AccountConfig, Config, GatewayConfig, GatewayType, GenesisConfig, NetworkConfig, - ObjectConfig, PersistedConfig, WalletConfig, AUTHORITIES_DB_NAME, SUI_GATEWAY_CONFIG, + Config, GatewayConfig, GatewayType, PersistedConfig, WalletConfig, SUI_GATEWAY_CONFIG, SUI_NETWORK_CONFIG, SUI_WALLET_CONFIG, }, keystore::KeystoreType, - sui_commands::{SuiCommand, SuiNetwork, SUI_AUTHORITY_KEYS}, + sui_commands::{SuiCommand, SuiNetwork}, wallet_commands::{WalletCommandResult, WalletCommands, WalletContext}, }; +use sui_config::{AccountConfig, GenesisConfig, NetworkConfig, ObjectConfig}; use sui_core::gateway_state::gateway_responses::SwitchResponse; use sui_core::sui_json::SuiJsonValue; use sui_types::{ base_types::{ObjectID, SequenceNumber, SuiAddress}, - crypto::{get_key_pair, random_key_pairs}, + crypto::get_key_pair, gas_coin::GasCoin, messages::TransactionEffects, object::{Object, ObjectRead, GAS_VALUE_FOR_TESTING}, }; -use crate::cli_tests::sui_network::start_test_network; +use test_utils::network::start_test_network; const TEST_DATA_DIR: &str = "src/unit_tests/data/"; const AIRDROP_SOURCE_CONTRACT_ADDRESS: &str = "bc4ca0eda7647a8ab7c2061c2e118a18a936f13d"; @@ -38,8 +38,6 @@ const AIRDROP_SOURCE_TOKEN_ID: u64 = 101u64; const AIRDROP_TOKEN_NAME: &str = "BoredApeYachtClub"; const AIRDROP_TOKEN_URI: &str = "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/101"; -mod sui_network; - macro_rules! retry_assert { ($test:expr, $timeout:expr) => {{ let mut duration = Duration::from_secs(0); @@ -83,24 +81,22 @@ async fn test_genesis() -> Result<(), anyhow::Error> { .flat_map(|r| r.map(|file| file.file_name().to_str().unwrap().to_owned())) .collect::>(); - assert_eq!(6, files.len()); + assert_eq!(4, files.len()); assert!(files.contains(&SUI_WALLET_CONFIG.to_string())); assert!(files.contains(&SUI_GATEWAY_CONFIG.to_string())); - assert!(files.contains(&AUTHORITIES_DB_NAME.to_string())); assert!(files.contains(&SUI_NETWORK_CONFIG.to_string())); assert!(files.contains(&"wallet.key".to_string())); - assert!(files.contains(&SUI_AUTHORITY_KEYS.to_string())); // Check network config let network_conf = PersistedConfig::::read(&working_dir.join(SUI_NETWORK_CONFIG))?; - assert_eq!(4, network_conf.authorities.len()); + assert_eq!(4, network_conf.validator_configs().len()); // Check wallet config let wallet_conf = PersistedConfig::::read(&working_dir.join(SUI_WALLET_CONFIG))?; if let GatewayType::Embedded(config) = &wallet_conf.gateway { - assert_eq!(4, config.authorities.len()); + assert_eq!(4, config.validator_set.len()); assert_eq!(working_dir.join("client_db"), config.db_folder_path); } else { panic!() @@ -172,7 +168,7 @@ async fn test_addresses_command() -> Result<(), anyhow::Error> { async fn test_cross_chain_airdrop() -> Result<(), anyhow::Error> { let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; + let network = start_test_network(working_dir.path(), None).await?; // Create Wallet context with the oracle account let wallet_conf_path = working_dir.path().join(SUI_WALLET_CONFIG); @@ -338,18 +334,8 @@ async fn test_custom_genesis() -> Result<(), anyhow::Error> { let working_dir = tempfile::tempdir()?; // Create and save genesis config file // Create 4 authorities, 1 account with 1 gas object with custom id - let key_pairs = random_key_pairs(4); - - let mut config = GenesisConfig::default_genesis( - working_dir.path(), - Some(( - key_pairs - .iter() - .map(|kp| *kp.public_key_bytes()) - .collect::>(), - key_pairs[0].copy(), - )), - )?; + + let mut config = GenesisConfig::for_local_testing()?; config.accounts.clear(); let object_id = ObjectID::random(); config.accounts.push(AccountConfig { @@ -361,7 +347,7 @@ async fn test_custom_genesis() -> Result<(), anyhow::Error> { gas_object_ranges: None, }); - let network = start_test_network(working_dir.path(), Some(config), Some(key_pairs)).await?; + let network = start_test_network(working_dir.path(), Some(config)).await?; // Wallet config let mut context = WalletContext::new(&working_dir.path().join(SUI_WALLET_CONFIG))?; @@ -402,20 +388,7 @@ async fn test_custom_genesis_with_custom_move_package() -> Result<(), anyhow::Er // Create and save genesis config file // Create 4 authorities and 1 account let num_authorities = 4; - let key_pairs = random_key_pairs(num_authorities); - let mut config = GenesisConfig::custom_genesis( - working_dir, - num_authorities, - 1, - 1, - Some(( - key_pairs - .iter() - .map(|kp| *kp.public_key_bytes()) - .collect::>(), - key_pairs[0].copy(), - )), - )?; + let mut config = GenesisConfig::custom_genesis(num_authorities, 1, 1)?; config .move_packages .push(PathBuf::from(TEST_DATA_DIR).join("custom_genesis_package_1")); @@ -424,16 +397,16 @@ async fn test_custom_genesis_with_custom_move_package() -> Result<(), anyhow::Er .push(PathBuf::from(TEST_DATA_DIR).join("custom_genesis_package_2")); // Start network - let network = start_test_network(working_dir, Some(config), Some(key_pairs)).await?; + let network = start_test_network(working_dir, Some(config)).await?; assert!(logs_contain("Loading 2 Move packages")); // Checks network config contains package ids let network_conf = PersistedConfig::::read(&working_dir.join(SUI_NETWORK_CONFIG))?; - assert_eq!(2, network_conf.loaded_move_packages.len()); + assert_eq!(2, network_conf.loaded_move_packages().len()); // Make sure we log out package id - for (_, id) in &network_conf.loaded_move_packages { + for (_, id) in network_conf.loaded_move_packages() { assert!(logs_contain(&*format!("{id}"))); } @@ -1050,7 +1023,7 @@ fn test_bug_1078() { #[tokio::test] async fn test_switch_command() -> Result<(), anyhow::Error> { let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; + let network = start_test_network(working_dir.path(), None).await?; // Create Wallet context. let wallet_conf = working_dir.path().join(SUI_WALLET_CONFIG); @@ -1145,7 +1118,7 @@ async fn test_switch_command() -> Result<(), anyhow::Error> { #[tokio::test] async fn test_active_address_command() -> Result<(), anyhow::Error> { let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; + let network = start_test_network(working_dir.path(), None).await?; // Create Wallet context. let wallet_conf = working_dir.path().join(SUI_WALLET_CONFIG); @@ -1366,7 +1339,7 @@ async fn setup_network_and_wallet() -> Result<(SuiNetwork, WalletContext, SuiAdd { let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; + let network = start_test_network(working_dir.path(), None).await?; // Create Wallet context. let wallet_conf = working_dir.path().join(SUI_WALLET_CONFIG); diff --git a/sui/src/unit_tests/rest_server_tests.rs b/sui/src/unit_tests/rest_server_tests.rs deleted file mode 100644 index 6c24e7370901b..0000000000000 --- a/sui/src/unit_tests/rest_server_tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -use anyhow::anyhow; -use dropshot::test_util::{LogContext, TestContext}; -use dropshot::{ConfigDropshot, ConfigLogging, ConfigLoggingLevel}; -use futures::future::try_join_all; -use http::{Method, StatusCode}; -use sui::SUI_WALLET_CONFIG; - -use sui::wallet_commands::WalletContext; - -use crate::rest_server_tests::sui_network::start_test_network; -use crate::{create_api, ServerContext}; - -mod sui_network; - -#[tokio::test] -async fn test_concurrency() -> Result<(), anyhow::Error> { - let api = create_api(); - - let config_dropshot: ConfigDropshot = Default::default(); - let log_config = ConfigLogging::StderrTerminal { - level: ConfigLoggingLevel::Debug, - }; - let logctx = LogContext::new("test_name", &log_config); - - let log = log_config - .to_logger("rest_server") - .map_err(|error| anyhow!("failed to create logger: {error}"))?; - - // Start sui network - let working_dir = tempfile::tempdir()?; - let network = start_test_network(working_dir.path(), None, None).await?; - let wallet = WalletContext::new(&working_dir.path().join(SUI_WALLET_CONFIG))?; - let address = wallet.config.accounts.first().unwrap(); - let documentation = api.openapi("Sui API", "0.1").json()?; - - let api_context = ServerContext::new(documentation, wallet.gateway); - let testctx = TestContext::new(api, api_context, &config_dropshot, Some(logctx), log); - let url = format!("/api/objects?address={}", address); - - let task = (0..10).map(|_| { - testctx - .client_testctx - .make_request_no_body(Method::GET, &url, StatusCode::OK) - }); - - let task = task - .into_iter() - .map(|request| async { request.await }) - .collect::>(); - - try_join_all(task).await.map_err(|e| anyhow!(e.message))?; - - network.kill().await?; - Ok(()) -} diff --git a/sui/src/unit_tests/rpc_server_tests.rs b/sui/src/unit_tests/rpc_server_tests.rs index feebd12844570..bfe7019b24874 100644 --- a/sui/src/unit_tests/rpc_server_tests.rs +++ b/sui/src/unit_tests/rpc_server_tests.rs @@ -31,9 +31,7 @@ use sui_types::{ SUI_FRAMEWORK_ADDRESS, }; -use crate::rpc_server_tests::sui_network::start_test_network; - -mod sui_network; +use test_utils::network::start_test_network; #[tokio::test] async fn test_get_objects() -> Result<(), anyhow::Error> { @@ -267,7 +265,7 @@ async fn test_get_transaction() -> Result<(), anyhow::Error> { async fn setup_test_network() -> Result { let working_dir = tempfile::tempdir()?.path().to_path_buf(); - let _network = start_test_network(&working_dir, None, None).await?; + let _network = start_test_network(&working_dir, None).await?; let (server_addr, rpc_server_handle) = start_rpc_gateway(&working_dir.join(SUI_GATEWAY_CONFIG)).await?; let wallet_conf: WalletConfig = PersistedConfig::read(&working_dir.join(SUI_WALLET_CONFIG))?; diff --git a/sui/tests/shared_objects_tests.rs b/sui/tests/shared_objects_tests.rs index 1b31a9b3a3e12..b4d4feec4d03b 100644 --- a/sui/tests/shared_objects_tests.rs +++ b/sui/tests/shared_objects_tests.rs @@ -3,7 +3,7 @@ use std::sync::Arc; -use sui::config::AuthorityInfo; +use sui_config::ValidatorInfo; use sui_core::{ authority_client::{AuthorityAPI, NetworkAuthorityClient}, gateway_state::{GatewayAPI, GatewayState}, @@ -29,7 +29,7 @@ use test_utils::{ /// Submit a certificate containing only owned-objects to all authorities. async fn submit_single_owner_transaction( transaction: Transaction, - configs: &[AuthorityInfo], + configs: &[ValidatorInfo], ) -> Vec { let certificate = make_certificates(vec![transaction]).pop().unwrap(); let txn = ConfirmationTransaction { certificate }; @@ -46,8 +46,8 @@ async fn submit_single_owner_transaction( responses } -fn get_client(config: &AuthorityInfo) -> NetworkAuthorityClient { - NetworkAuthorityClient::connect_lazy(&config.network_address).unwrap() +fn get_client(config: &ValidatorInfo) -> NetworkAuthorityClient { + NetworkAuthorityClient::connect_lazy(config.network_address()).unwrap() } /// Keep submitting the certificates of a shared-object transaction until it is sequenced by @@ -55,7 +55,7 @@ fn get_client(config: &AuthorityInfo) -> NetworkAuthorityClient { /// may drop transactions. The certificate is submitted to every Sui authority. async fn submit_shared_object_transaction( transaction: Transaction, - configs: &[AuthorityInfo], + configs: &[ValidatorInfo], ) -> Vec> { let certificate = make_certificates(vec![transaction]).pop().unwrap(); let message = ConsensusTransaction::UserTransaction(certificate); @@ -84,7 +84,7 @@ async fn submit_shared_object_transaction( } /// Helper function to publish the move package of a simple shared counter. -async fn publish_counter_package(gas_object: Object, configs: &[AuthorityInfo]) -> ObjectRef { +async fn publish_counter_package(gas_object: Object, configs: &[ValidatorInfo]) -> ObjectRef { let transaction = publish_move_package_transaction(gas_object); let replies = submit_single_owner_transaction(transaction, configs).await; let mut package_refs = Vec::new(); @@ -104,8 +104,8 @@ async fn shared_object_transaction() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(objects, &configs, &key_pairs).await; + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(objects, &configs).await; // Make a test shared object certificate. let transaction = test_shared_object_transactions().pop().unwrap(); @@ -113,7 +113,7 @@ async fn shared_object_transaction() { // Submit the transaction. Note that this transaction is random and we do not expect // it to be successfully executed by the Move execution engine. tokio::task::yield_now().await; - let reply = submit_shared_object_transaction(transaction, &configs[0..1]) + let reply = submit_shared_object_transaction(transaction, &configs.validator_set()[0..1]) .await .pop() .unwrap(); @@ -129,16 +129,17 @@ async fn many_shared_object_transactions() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(objects, &configs, &key_pairs).await; + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(objects, &configs).await; // Make a test shared object certificate. + tokio::time::sleep(std::time::Duration::from_secs(3)).await; let transaction = test_shared_object_transactions().pop().unwrap(); // Submit the transaction. Note that this transaction is random and we do not expect // it to be successfully executed by the Move execution engine. tokio::task::yield_now().await; - let replies = submit_shared_object_transaction(transaction, &configs).await; + let replies = submit_shared_object_transaction(transaction, configs.validator_set()).await; for reply in replies { match reply { Ok(_) => (), @@ -155,12 +156,14 @@ async fn call_shared_object_contract() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(gas_objects.clone(), &configs, &key_pairs).await; + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(gas_objects.clone(), &configs).await; // Publish the move package to all authorities and get the new package ref. tokio::task::yield_now().await; - let package_ref = publish_counter_package(gas_objects.pop().unwrap(), &configs).await; + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + let package_ref = + publish_counter_package(gas_objects.pop().unwrap(), configs.validator_set()).await; // Make a transaction to create a counter. tokio::task::yield_now().await; @@ -171,7 +174,7 @@ async fn call_shared_object_contract() { package_ref, /* arguments */ Vec::default(), ); - let replies = submit_single_owner_transaction(transaction, &configs).await; + let replies = submit_single_owner_transaction(transaction, configs.validator_set()).await; let mut counter_ids = Vec::new(); for reply in replies { let effects = reply.signed_effects.unwrap().effects; @@ -193,7 +196,7 @@ async fn call_shared_object_contract() { CallArg::Pure(0u64.to_le_bytes().to_vec()), ], ); - let reply = submit_shared_object_transaction(transaction, &configs[0..1]) + let reply = submit_shared_object_transaction(transaction, &configs.validator_set()[0..1]) .await .pop() .unwrap(); @@ -210,7 +213,7 @@ async fn call_shared_object_contract() { package_ref, vec![CallArg::SharedObject(counter_id)], ); - let reply = submit_shared_object_transaction(transaction, &configs[0..1]) + let reply = submit_shared_object_transaction(transaction, &configs.validator_set()[0..1]) .await .pop() .unwrap(); @@ -230,7 +233,7 @@ async fn call_shared_object_contract() { CallArg::Pure(1u64.to_le_bytes().to_vec()), ], ); - let reply = submit_shared_object_transaction(transaction, &configs[0..1]) + let reply = submit_shared_object_transaction(transaction, &configs.validator_set()[0..1]) .await .pop() .unwrap(); @@ -247,12 +250,13 @@ async fn shared_object_flood() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(gas_objects.clone(), &configs, &key_pairs).await; + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(gas_objects.clone(), &configs).await; // Publish the move package to all authorities and get the new package ref. tokio::task::yield_now().await; - let package_ref = publish_counter_package(gas_objects.pop().unwrap(), &configs).await; + let package_ref = + publish_counter_package(gas_objects.pop().unwrap(), configs.validator_set()).await; // Make a transaction to create a counter. tokio::task::yield_now().await; @@ -263,7 +267,7 @@ async fn shared_object_flood() { package_ref, /* arguments */ Vec::default(), ); - let replies = submit_single_owner_transaction(transaction, &configs).await; + let replies = submit_single_owner_transaction(transaction, configs.validator_set()).await; let mut counter_ids = Vec::new(); for reply in replies { let effects = reply.signed_effects.unwrap().effects; @@ -285,7 +289,7 @@ async fn shared_object_flood() { CallArg::Pure(0u64.to_le_bytes().to_vec()), ], ); - let replies = submit_shared_object_transaction(transaction, &configs).await; + let replies = submit_shared_object_transaction(transaction, configs.validator_set()).await; for reply in replies { match reply { Ok(info) => { @@ -305,7 +309,7 @@ async fn shared_object_flood() { package_ref, vec![CallArg::SharedObject(counter_id)], ); - let replies = submit_shared_object_transaction(transaction, &configs).await; + let replies = submit_shared_object_transaction(transaction, configs.validator_set()).await; for reply in replies { match reply { Ok(info) => { @@ -328,7 +332,7 @@ async fn shared_object_flood() { CallArg::Pure(1u64.to_le_bytes().to_vec()), ], ); - let replies = submit_shared_object_transaction(transaction, &configs).await; + let replies = submit_shared_object_transaction(transaction, configs.validator_set()).await; for reply in replies { match reply { Ok(info) => { @@ -346,12 +350,13 @@ async fn shared_object_sync() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(gas_objects.clone(), &configs, &key_pairs).await; + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(gas_objects.clone(), &configs).await; // Publish the move package to all authorities and get the new package ref. tokio::task::yield_now().await; - let package_ref = publish_counter_package(gas_objects.pop().unwrap(), &configs).await; + let package_ref = + publish_counter_package(gas_objects.pop().unwrap(), configs.validator_set()).await; // Send a transaction to create a counter, but only to one authority. tokio::task::yield_now().await; @@ -362,8 +367,11 @@ async fn shared_object_sync() { package_ref, /* arguments */ Vec::default(), ); - let mut replies = - submit_single_owner_transaction(create_counter_transaction.clone(), &configs[0..1]).await; + let mut replies = submit_single_owner_transaction( + create_counter_transaction.clone(), + &configs.validator_set()[0..1], + ) + .await; let reply = replies.pop().unwrap(); let effects = reply.signed_effects.unwrap().effects; assert!(matches!(effects.status, ExecutionStatus::Success { .. })); @@ -380,19 +388,23 @@ async fn shared_object_sync() { ); // Let's submit the transaction to the first authority (the only one up-to-date). - let reply = - submit_shared_object_transaction(increment_counter_transaction.clone(), &configs[0..1]) - .await - .pop() - .unwrap(); + let reply = submit_shared_object_transaction( + increment_counter_transaction.clone(), + &configs.validator_set()[0..1], + ) + .await + .pop() + .unwrap(); let info = reply.unwrap(); let effects = info.signed_effects.unwrap().effects; assert!(matches!(effects.status, ExecutionStatus::Success { .. })); // Let's submit the transaction to the out-of-date authorities. - let replies = - submit_shared_object_transaction(increment_counter_transaction.clone(), &configs[1..]) - .await; + let replies = submit_shared_object_transaction( + increment_counter_transaction.clone(), + &configs.validator_set()[1..], + ) + .await; for reply in replies { match reply { // Right now grpc doesn't send back the error message in a recoverable way @@ -405,7 +417,8 @@ async fn shared_object_sync() { // Now send the missing certificates to the outdated authorities. We also re-send // the transaction to the first authority who should simply ignore it. tokio::task::yield_now().await; - let replies = submit_single_owner_transaction(create_counter_transaction, &configs).await; + let replies = + submit_single_owner_transaction(create_counter_transaction, configs.validator_set()).await; for reply in replies { let effects = reply.signed_effects.unwrap().effects; assert!(matches!(effects.status, ExecutionStatus::Success { .. })); @@ -413,8 +426,11 @@ async fn shared_object_sync() { // Now we can try again with the shared-object transaction who failed before. tokio::task::yield_now().await; - let replies = - submit_shared_object_transaction(increment_counter_transaction, &configs[1..]).await; + let replies = submit_shared_object_transaction( + increment_counter_transaction, + &configs.validator_set()[1..], + ) + .await; for reply in replies { match reply { Ok(info) => { @@ -432,15 +448,16 @@ async fn shared_object_on_gateway() { // Get the authority configs and spawn them. Note that it is important to not drop // the handles (or the authorities will stop). - let (configs, key_pairs) = test_authority_configs(); - let _handles = spawn_test_authorities(gas_objects.clone(), &configs, &key_pairs).await; - let clients = create_authority_aggregator(&configs); + let configs = test_authority_configs(); + let _handles = spawn_test_authorities(gas_objects.clone(), &configs).await; + let clients = create_authority_aggregator(configs.validator_set()); let path = tempfile::tempdir().unwrap().into_path(); let gateway = Arc::new(GatewayState::new_with_authorities(path, clients).unwrap()); // Publish the move package to all authorities and get the new package ref. tokio::task::yield_now().await; - let package_ref = publish_counter_package(gas_objects.pop().unwrap(), &configs).await; + let package_ref = + publish_counter_package(gas_objects.pop().unwrap(), configs.validator_set()).await; // Send a transaction to create a counter. tokio::task::yield_now().await; diff --git a/sui_core/src/authority_server.rs b/sui_core/src/authority_server.rs index 4d316217eb816..52391c98e3492 100644 --- a/sui_core/src/authority_server.rs +++ b/sui_core/src/authority_server.rs @@ -72,7 +72,7 @@ impl AuthorityServer { consensus_address, state.committee.clone(), tx_consensus_listener, - /* max_delay */ Duration::from_millis(2_000), + /* max_delay */ Duration::from_millis(5_000), ); Self { diff --git a/sui_types/src/base_types.rs b/sui_types/src/base_types.rs index d85ed48d53d5b..cbe4ac40ca6f5 100644 --- a/sui_types/src/base_types.rs +++ b/sui_types/src/base_types.rs @@ -137,6 +137,12 @@ impl TryFrom> for SuiAddress { impl From<&PublicKeyBytes> for SuiAddress { fn from(key: &PublicKeyBytes) -> SuiAddress { + Self::from(*key) + } +} + +impl From for SuiAddress { + fn from(key: PublicKeyBytes) -> SuiAddress { let mut hasher = Sha3_256::default(); hasher.update(key.as_ref()); let g_arr = hasher.finalize(); diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index 2a427354ee063..4640337074cc3 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -7,6 +7,7 @@ publish = false edition = "2021" [dependencies] +anyhow = { version = "1.0.57", features = ["backtrace"] } tokio = { version = "1.18.2", features = ["sync", "rt"] } tokio-util = { version = "0.7.1", features = ["codec"] } bytes = "1.1.0" @@ -25,6 +26,7 @@ move-core-types = { git = "https://github.com/move-language/move", rev = "8958b4 typed-store = { git = "https://github.com/MystenLabs/mysten-infra", rev ="7c247967e5a5abd59ecaa75bc62b05bcdf4503fe"} narwhal-config = { git = "https://github.com/MystenLabs/narwhal", rev = "23745f48103656eae4a4205d0b3edd53ad8894de", package = "config" } +sui-config = { path = "../crates/sui-config" } sui-types = { path = "../sui_types" } sui_core = { path = "../sui_core" } sui-network = { path = "../crates/sui-network" } diff --git a/test_utils/src/authority.rs b/test_utils/src/authority.rs index aec1c730db0d3..8c9885c79b340 100644 --- a/test_utils/src/authority.rs +++ b/test_utils/src/authority.rs @@ -1,13 +1,12 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{test_committee, test_keys}; -use narwhal_config::Parameters as ConsensusParameters; -use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; -use sui::{ - config::{make_default_narwhal_committee, utils::get_available_port, AuthorityInfo}, - sui_commands::make_authority, -}; + +use crate::TEST_COMMITTEE_SIZE; +use rand::{prelude::StdRng, SeedableRng}; +use std::{collections::BTreeMap, sync::Arc}; +use sui::sui_commands::make_authority; use sui_adapter::genesis; +use sui_config::{NetworkConfig, ValidatorInfo}; use sui_core::{ authority::{AuthorityState, AuthorityStore}, authority_aggregator::AuthorityAggregator, @@ -15,7 +14,7 @@ use sui_core::{ authority_server::AuthorityServerHandle, safe_client::SafeClient, }; -use sui_types::{committee::Committee, crypto::KeyPair, object::Object}; +use sui_types::{committee::Committee, object::Object}; /// The default network buffer size of a test authority. pub const NETWORK_BUFFER_SIZE: usize = 65_000; @@ -27,47 +26,26 @@ pub fn test_authority_store() -> AuthorityStore { } /// Make an authority config for each of the `TEST_COMMITTEE_SIZE` authorities in the test committee. -pub fn test_authority_configs() -> (Vec, Vec) { - let test_keys = test_keys(); - let key_pair = test_keys - .iter() - .map(|(_, key_pair)| key_pair.copy()) - .collect(); - let authorities = test_keys - .into_iter() - .map(|(address, key)| { - let authority_port = get_available_port(); - let consensus_port = get_available_port(); - - AuthorityInfo { - address, - public_key: *key.public_key_bytes(), - network_address: format!("/ip4/127.0.0.1/tcp/{authority_port}/http") - .parse() - .unwrap(), - db_path: PathBuf::new(), - stake: 1, - consensus_address: format!("/ip4/127.0.0.1/tcp/{consensus_port}/http") - .parse() - .unwrap(), - } - }) - .collect(); - (authorities, key_pair) +pub fn test_authority_configs() -> NetworkConfig { + let config_dir = tempfile::tempdir().unwrap().into_path(); + let rng = StdRng::from_seed([0; 32]); + NetworkConfig::generate_with_rng(&config_dir, TEST_COMMITTEE_SIZE, rng) } -/// Make a test authority state for each committee member. -pub async fn test_authority_states(objects: I) -> Vec +/// Spawn all authorities in the test committee into a separate tokio task. +pub async fn spawn_test_authorities( + objects: I, + config: &NetworkConfig, +) -> Vec where I: IntoIterator + Clone, { - let committee = test_committee(); - let mut authorities = Vec::new(); - for (_, key) in test_keys() { + let mut handles = Vec::new(); + for validator in config.validator_configs() { let state = AuthorityState::new( - committee.clone(), - *key.public_key_bytes(), - Arc::pin(key), + validator.committee_config().committee(), + validator.public_key(), + Arc::pin(validator.key_pair().copy()), Arc::new(test_authority_store()), genesis::clone_genesis_compiled_modules(), &mut genesis::get_genesis_context(), @@ -75,72 +53,37 @@ where .await; for o in objects.clone() { - state.insert_genesis_object(o).await; + state.insert_genesis_object(o).await } - authorities.push(state); - } - authorities -} - -/// Spawn all authorities in the test committee into a separate tokio task. -pub async fn spawn_test_authorities( - objects: I, - authorities: &[AuthorityInfo], - key_pairs: &[KeyPair], -) -> Vec -where - I: IntoIterator + Clone, -{ - let states = test_authority_states(objects).await; - let consensus_committee = make_default_narwhal_committee(authorities).unwrap(); - let mut handles = Vec::new(); - for ((state, config), key_pair) in states - .into_iter() - .zip(authorities.iter()) - .zip(key_pairs.iter()) - { - let consensus_parameters = ConsensusParameters { - max_header_delay: std::time::Duration::from_millis(200), - header_size: 1, - ..ConsensusParameters::default() - }; - let handle = make_authority( - /* authority */ config, - key_pair, - state, - &consensus_committee, - /* consensus_store_path */ tempfile::tempdir().unwrap().path(), - &consensus_parameters, - /* net_parameters */ None, - ) - .await - .unwrap() - .spawn() - .await - .unwrap(); + let handle = make_authority(validator, state) + .await + .unwrap() + .spawn() + .await + .unwrap(); handles.push(handle); } handles } pub fn create_authority_aggregator( - authority_configs: &[AuthorityInfo], + authority_configs: &[ValidatorInfo], ) -> AuthorityAggregator> { let voting_rights: BTreeMap<_, _> = authority_configs .iter() - .map(|config| (config.public_key, config.stake)) + .map(|config| (config.public_key(), config.stake())) .collect(); let committee = Committee::new(0, voting_rights); let clients: BTreeMap<_, _> = authority_configs .iter() .map(|config| { ( - config.public_key, + config.public_key(), SafeClient::new( - NetworkAuthorityClient::connect_lazy(&config.network_address).unwrap(), + NetworkAuthorityClient::connect_lazy(config.network_address()).unwrap(), committee.clone(), - config.public_key, + config.public_key(), ), ) }) diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 34ae104ec431f..fd50262718829 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub mod authority; pub mod messages; +pub mod network; pub mod objects; use rand::{rngs::StdRng, SeedableRng}; diff --git a/test_utils/src/network.rs b/test_utils/src/network.rs new file mode 100644 index 0000000000000..1cd6d21da70c1 --- /dev/null +++ b/test_utils/src/network.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::path::Path; +use sui::{ + config::{ + Config, GatewayConfig, GatewayType, WalletConfig, SUI_GATEWAY_CONFIG, SUI_NETWORK_CONFIG, + SUI_WALLET_CONFIG, + }, + keystore::KeystoreType, + sui_commands::{genesis, SuiNetwork}, +}; +use sui_config::GenesisConfig; + +const NUM_VALIDAOTR: usize = 4; + +pub async fn start_test_network( + working_dir: &Path, + genesis_config: Option, +) -> Result { + std::fs::create_dir_all(working_dir)?; + let working_dir = working_dir.to_path_buf(); + let network_path = working_dir.join(SUI_NETWORK_CONFIG); + let wallet_path = working_dir.join(SUI_WALLET_CONFIG); + let keystore_path = working_dir.join("wallet.key"); + let db_folder_path = working_dir.join("client_db"); + + let genesis_config = match genesis_config { + Some(genesis_config) => genesis_config, + None => { + let mut config = GenesisConfig::for_local_testing()?; + config.committee_size = NUM_VALIDAOTR; + config + } + }; + let (network_config, accounts, mut keystore) = genesis(genesis_config).await?; + let network = SuiNetwork::start(&network_config).await?; + + let network_config = network_config.persisted(&network_path); + network_config.save()?; + keystore.set_path(&keystore_path); + keystore.save()?; + + let validators = network_config.validator_set().to_owned(); + let active_address = accounts.get(0).copied(); + + GatewayConfig { + db_folder_path: db_folder_path.clone(), + validator_set: validators.clone(), + ..Default::default() + } + .persisted(&working_dir.join(SUI_GATEWAY_CONFIG)) + .save()?; + + // Create wallet config with stated authorities port + WalletConfig { + accounts, + keystore: KeystoreType::File(keystore_path), + gateway: GatewayType::Embedded(GatewayConfig { + db_folder_path, + validator_set: validators, + ..Default::default() + }), + active_address, + } + .persisted(&wallet_path) + .save()?; + + // Return network handle + Ok(network) +}