diff --git a/zkstack_cli/crates/config/src/consts.rs b/zkstack_cli/crates/config/src/consts.rs index c3efb4ac3e96..1332d59037f4 100644 --- a/zkstack_cli/crates/config/src/consts.rs +++ b/zkstack_cli/crates/config/src/consts.rs @@ -36,8 +36,8 @@ pub(crate) const LOCAL_APPS_PATH: &str = "apps/"; pub(crate) const LOCAL_CHAINS_PATH: &str = "chains/"; pub(crate) const LOCAL_CONFIGS_PATH: &str = "configs/"; pub(crate) const LOCAL_GENERATED_PATH: &str = ".generated/"; -pub(crate) const LOCAL_DB_PATH: &str = "db/"; -pub(crate) const LOCAL_ARTIFACTS_PATH: &str = "artifacts/"; +pub const LOCAL_DB_PATH: &str = "db/"; +pub const LOCAL_ARTIFACTS_PATH: &str = "artifacts/"; /// Name of apps config file pub const APPS_CONFIG_FILE: &str = "apps.yaml"; diff --git a/zkstack_cli/crates/config/src/lib.rs b/zkstack_cli/crates/config/src/lib.rs index f3001fd55f8d..4937618ae1ed 100644 --- a/zkstack_cli/crates/config/src/lib.rs +++ b/zkstack_cli/crates/config/src/lib.rs @@ -14,7 +14,6 @@ pub use zksync_protobuf_config::{encode_yaml_repr, read_yaml_repr}; mod apps; mod chain; -mod consts; mod contracts; mod ecosystem; mod file_config; @@ -28,6 +27,7 @@ mod wallets; pub mod consensus_config; pub mod consensus_secrets; +pub mod consts; pub mod docker_compose; pub mod explorer; pub mod explorer_compose; diff --git a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh index fa24bf741c22..d183494626bf 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -82,6 +82,7 @@ in-file\:"Specify file with wallets"))' \ '--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR:_default' \ '--set-as-default=[Set as default chain]' \ '--evm-emulator=[Enable EVM emulator]' \ +'--l1-network=[L1 Network]:L1_NETWORK:(localhost sepolia holesky mainnet)' \ '--start-containers=[Start reth and postgres containers after creation]' \ '--chain=[Chain to use]:CHAIN:_default' \ '--legacy-bridge[]' \ @@ -243,6 +244,7 @@ in-file\:"Specify file with wallets"))' \ '--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR:_default' \ '--set-as-default=[Set as default chain]' \ '--evm-emulator=[Enable EVM emulator]' \ +'--l1-network=[L1 Network]:L1_NETWORK:(localhost sepolia holesky mainnet)' \ '--chain=[Chain to use]:CHAIN:_default' \ '--legacy-bridge[]' \ '-v[Verbose mode]' \ diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index 0ff8584ba2a1..03ecb0ee28ac 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -71,6 +71,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l base-token-price-denominator -d 'Base token denominator' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "{true\t'',false\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l l1-network -d 'L1 Network' -r -f -a "{localhost\t'',sepolia\t'',holesky\t'',mainnet\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l start-containers -d 'Start reth and postgres containers after creation' -r -f -a "{true\t'',false\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l legacy-bridge @@ -159,6 +160,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l base-token-price-denominator -d 'Base token denominator' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "{true\t'',false\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l l1-network -d 'L1 Network' -r -f -a "{localhost\t'',sepolia\t'',holesky\t'',mainnet\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l legacy-bridge complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -s v -l verbose -d 'Verbose mode' diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.sh b/zkstack_cli/crates/zkstack/completion/zkstack.sh index 941c964d4c60..f235d7f8fec8 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.sh +++ b/zkstack_cli/crates/zkstack/completion/zkstack.sh @@ -1652,7 +1652,7 @@ _zkstack() { return 0 ;; zkstack__chain__create) - opts="-v -h --chain-name --chain-id --prover-mode --wallet-creation --wallet-path --l1-batch-commit-data-generator-mode --base-token-address --base-token-price-nominator --base-token-price-denominator --set-as-default --legacy-bridge --evm-emulator --verbose --chain --ignore-prerequisites --help" + opts="-v -h --chain-name --chain-id --prover-mode --wallet-creation --wallet-path --l1-batch-commit-data-generator-mode --base-token-address --base-token-price-nominator --base-token-price-denominator --set-as-default --legacy-bridge --evm-emulator --l1-network --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1713,6 +1713,10 @@ _zkstack() { COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 ;; + --l1-network) + COMPREPLY=($(compgen -W "localhost sepolia holesky mainnet" -- "${cur}")) + return 0 + ;; --chain) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -5305,7 +5309,7 @@ _zkstack() { return 0 ;; zkstack__ecosystem__create) - opts="-v -h --ecosystem-name --l1-network --link-to-code --chain-name --chain-id --prover-mode --wallet-creation --wallet-path --l1-batch-commit-data-generator-mode --base-token-address --base-token-price-nominator --base-token-price-denominator --set-as-default --legacy-bridge --evm-emulator --start-containers --verbose --chain --ignore-prerequisites --help" + opts="-v -h --ecosystem-name --l1-network --link-to-code --chain-name --chain-id --prover-mode --wallet-creation --wallet-path --l1-batch-commit-data-generator-mode --base-token-address --base-token-price-nominator --base-token-price-denominator --set-as-default --legacy-bridge --evm-emulator --l1-network --start-containers --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -5381,6 +5385,10 @@ _zkstack() { COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 ;; + --l1-network) + COMPREPLY=($(compgen -W "localhost sepolia holesky mainnet" -- "${cur}")) + return 0 + ;; --start-containers) COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs index ec37f9ba0304..c6f2f8b28362 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs @@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use strum::{Display, EnumIter, IntoEnumIterator}; use types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; -use zksync_basic_types::H160; +use xshell::Shell; +use zksync_basic_types::{L2ChainId, H160}; use crate::{ defaults::L2_CHAIN_ID, @@ -20,12 +21,13 @@ use crate::{ MSG_CHAIN_ID_PROMPT, MSG_CHAIN_ID_VALIDATOR_ERR, MSG_CHAIN_NAME_PROMPT, MSG_EVM_EMULATOR_HELP, MSG_EVM_EMULATOR_PROMPT, MSG_L1_BATCH_COMMIT_DATA_GENERATOR_MODE_PROMPT, MSG_L1_COMMIT_DATA_GENERATOR_MODE_HELP, - MSG_NUMBER_VALIDATOR_GREATHER_THAN_ZERO_ERR, MSG_NUMBER_VALIDATOR_NOT_ZERO_ERR, - MSG_PROVER_MODE_HELP, MSG_PROVER_VERSION_PROMPT, MSG_SET_AS_DEFAULT_HELP, - MSG_SET_AS_DEFAULT_PROMPT, MSG_WALLET_CREATION_HELP, MSG_WALLET_CREATION_PROMPT, - MSG_WALLET_CREATION_VALIDATOR_ERR, MSG_WALLET_PATH_HELP, MSG_WALLET_PATH_INVALID_ERR, - MSG_WALLET_PATH_PROMPT, + MSG_L1_NETWORK_HELP, MSG_L1_NETWORK_PROMPT, MSG_NUMBER_VALIDATOR_GREATHER_THAN_ZERO_ERR, + MSG_NUMBER_VALIDATOR_NOT_ZERO_ERR, MSG_PROVER_MODE_HELP, MSG_PROVER_VERSION_PROMPT, + MSG_SET_AS_DEFAULT_HELP, MSG_SET_AS_DEFAULT_PROMPT, MSG_WALLET_CREATION_HELP, + MSG_WALLET_CREATION_PROMPT, MSG_WALLET_CREATION_VALIDATOR_ERR, MSG_WALLET_PATH_HELP, + MSG_WALLET_PATH_INVALID_ERR, MSG_WALLET_PATH_PROMPT, }, + utils::link_to_code::get_link_to_code, }; // We need to duplicate it for using enum inside the arguments @@ -70,21 +72,30 @@ pub struct ChainCreateArgs { pub(crate) legacy_bridge: bool, #[arg(long, help = MSG_EVM_EMULATOR_HELP, default_missing_value = "true", num_args = 0..=1)] evm_emulator: Option, + #[clap(long, help = MSG_L1_NETWORK_HELP, value_enum)] + pub l1_network: Option, } impl ChainCreateArgs { + #[allow(clippy::too_many_arguments)] pub fn fill_values_with_prompt( self, + shell: &Shell, number_of_chains: u32, - l1_network: &L1Network, + internal_id: u32, + l1_network: Option, possible_erc20: Vec, - link_to_code: String, + link_to_code: Option, + chains_path: Option, + era_chain_id: L2ChainId, ) -> anyhow::Result { let mut chain_name = self .chain_name .unwrap_or_else(|| Prompt::new(MSG_CHAIN_NAME_PROMPT).ask()); chain_name = slugify!(&chain_name, separator = "_"); + let chain_path = chains_path.unwrap_or_default().join(&chain_name); + let chain_id = self .chain_id .map(|v| match v { @@ -97,8 +108,14 @@ impl ChainCreateArgs { .ask() }); + let l1_network = l1_network.unwrap_or_else(|| { + self.l1_network.unwrap_or_else(|| { + PromptSelect::new(MSG_L1_NETWORK_PROMPT, L1Network::iter()).ask() + }) + }); + let wallet_creation = if let Some(wallet) = self.wallet_creation { - if wallet == WalletCreation::Localhost && *l1_network != L1Network::Localhost { + if wallet == WalletCreation::Localhost && l1_network != L1Network::Localhost { bail!(MSG_WALLET_CREATION_VALIDATOR_ERR); } else { wallet @@ -108,7 +125,7 @@ impl ChainCreateArgs { MSG_WALLET_CREATION_PROMPT, WalletCreation::iter().filter(|wallet| { // Disable localhost wallets for external networks - if *l1_network == L1Network::Localhost { + if l1_network == L1Network::Localhost { true } else { *wallet != WalletCreation::Localhost @@ -215,6 +232,8 @@ impl ChainCreateArgs { } }; + let link_to_code = link_to_code.unwrap_or_else(|| get_link_to_code(shell)); + let evm_emulator = self.evm_emulator.unwrap_or_else(|| { PromptConfirm::new(MSG_EVM_EMULATOR_PROMPT) .default(false) @@ -222,6 +241,9 @@ impl ChainCreateArgs { }); let set_as_default = self.set_as_default.unwrap_or_else(|| { + if number_of_chains == 0 { + return true; + } PromptConfirm::new(MSG_SET_AS_DEFAULT_PROMPT) .default(true) .ask() @@ -239,6 +261,10 @@ impl ChainCreateArgs { legacy_bridge: self.legacy_bridge, evm_emulator, link_to_code, + chain_path, + era_chain_id, + internal_id, + l1_network, }) } } @@ -256,6 +282,10 @@ pub struct ChainCreateArgsFinal { pub legacy_bridge: bool, pub evm_emulator: bool, pub link_to_code: String, + pub chain_path: PathBuf, + pub era_chain_id: L2ChainId, + pub internal_id: u32, + pub l1_network: L1Network, } #[derive(Debug, Clone, EnumIter, Display, PartialEq, Eq)] diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs index c5ff4956c87b..4ef5f8573ed1 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs @@ -3,10 +3,10 @@ use std::cell::OnceCell; use anyhow::Context; use common::{logger, spinner::Spinner}; use config::{ - create_local_configs_dir, create_wallets, + create_local_configs_dir, create_wallets, get_default_era_chain_id, traits::{ReadConfigWithBasePath, SaveConfigWithBasePath}, zkstack_config::ZkStackConfig, - ChainConfig, EcosystemConfig, GenesisConfig, + ChainConfig, EcosystemConfig, GenesisConfig, LOCAL_ARTIFACTS_PATH, LOCAL_DB_PATH, }; use xshell::Shell; use zksync_basic_types::L2ChainId; @@ -22,22 +22,49 @@ use crate::{ }; pub fn run(args: ChainCreateArgs, shell: &Shell) -> anyhow::Result<()> { - let mut ecosystem_config = ZkStackConfig::ecosystem(shell)?; + let mut ecosystem_config = ZkStackConfig::ecosystem(shell).ok(); create(args, &mut ecosystem_config, shell) } fn create( args: ChainCreateArgs, - ecosystem_config: &mut EcosystemConfig, + ecosystem: &mut Option, shell: &Shell, ) -> anyhow::Result<()> { - let tokens = ecosystem_config.get_erc20_tokens(); + let possible_erc20 = ecosystem + .as_ref() + .map(|ecosystem| ecosystem.get_erc20_tokens()) + .unwrap_or_default(); + + let number_of_chains = ecosystem + .as_ref() + .map(|ecosystem| ecosystem.list_of_chains().len() as u32) + .unwrap_or(0); + + let internal_id = ecosystem.as_ref().map_or(0, |_| number_of_chains + 1); + + let l1_network = ecosystem.as_ref().map(|ecosystem| ecosystem.l1_network); + + let chains_path = ecosystem.as_ref().map(|ecosystem| ecosystem.chains.clone()); + let era_chain_id = ecosystem + .as_ref() + .map(|ecosystem| ecosystem.era_chain_id) + .unwrap_or(get_default_era_chain_id()); + + let link_to_code = ecosystem + .as_ref() + .map(|ecosystem| ecosystem.link_to_code.clone().display().to_string()); + let args = args .fill_values_with_prompt( - ecosystem_config.list_of_chains().len() as u32, - &ecosystem_config.l1_network, - tokens, - ecosystem_config.link_to_code.clone().display().to_string(), + shell, + number_of_chains, + internal_id, + l1_network, + possible_erc20, + link_to_code, + chains_path, + era_chain_id, ) .context(MSG_ARGS_VALIDATOR_ERR)?; @@ -47,10 +74,14 @@ fn create( let spinner = Spinner::new(MSG_CREATING_CHAIN_CONFIGURATIONS_SPINNER); let name = args.chain_name.clone(); let set_as_default = args.set_as_default; - create_chain_inner(args, ecosystem_config, shell)?; - if set_as_default { - ecosystem_config.default_chain = name; - ecosystem_config.save_with_base_path(shell, ".")?; + + create_chain_inner(args, shell)?; + + if let Some(ecosystem) = ecosystem.as_mut() { + if set_as_default { + ecosystem.default_chain = name; + ecosystem.save_with_base_path(shell, ".")?; + } } spinner.finish(); @@ -59,24 +90,19 @@ fn create( Ok(()) } -pub(crate) fn create_chain_inner( - args: ChainCreateArgsFinal, - ecosystem_config: &EcosystemConfig, - shell: &Shell, -) -> anyhow::Result<()> { +pub(crate) fn create_chain_inner(args: ChainCreateArgsFinal, shell: &Shell) -> anyhow::Result<()> { if args.legacy_bridge { logger::warn("WARNING!!! You are creating a chain with legacy bridge, use it only for testing compatibility") } let default_chain_name = args.chain_name.clone(); - let chain_path = ecosystem_config.chains.join(&default_chain_name); + let chain_path = args.chain_path; let chain_configs_path = create_local_configs_dir(shell, &chain_path)?; let (chain_id, legacy_bridge) = if args.legacy_bridge { // Legacy bridge is distinguished by using the same chain id as ecosystem - (ecosystem_config.era_chain_id, Some(true)) + (args.era_chain_id, Some(true)) } else { (L2ChainId::from(args.chain_id), None) }; - let internal_id = ecosystem_config.list_of_chains().len() as u32; let link_to_code = resolve_link_to_code(shell, chain_path.clone(), args.link_to_code.clone())?; let default_genesis_config = GenesisConfig::read_with_base_path( shell, @@ -86,16 +112,18 @@ pub(crate) fn create_chain_inner( if args.evm_emulator && !has_evm_emulation_support { anyhow::bail!(MSG_EVM_EMULATOR_HASH_MISSING_ERR); } + let rocks_db_path = chain_path.join(LOCAL_DB_PATH); + let artifacts = chain_path.join(LOCAL_ARTIFACTS_PATH); let chain_config = ChainConfig { - id: internal_id, + id: args.internal_id, name: default_chain_name.clone(), chain_id, prover_version: args.prover_version, - l1_network: ecosystem_config.l1_network, - link_to_code: ecosystem_config.link_to_code.clone(), - rocks_db_path: ecosystem_config.get_chain_rocks_db_path(&default_chain_name), - artifacts: ecosystem_config.get_chain_artifacts_path(&default_chain_name), + l1_network: args.l1_network, + link_to_code: link_to_code.clone(), + rocks_db_path, + artifacts, configs: chain_configs_path.clone(), external_node_config_path: None, l1_batch_commit_data_generator_mode: args.l1_batch_commit_data_generator_mode, @@ -109,8 +137,8 @@ pub(crate) fn create_chain_inner( create_wallets( shell, &chain_config.configs, - &ecosystem_config.link_to_code, - internal_id, + &link_to_code, + args.internal_id, args.wallet_creation, args.wallet_path, )?; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs index 53d9c27be60b..05cfeafd4752 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use clap::{Parser, ValueHint}; use common::{Prompt, PromptConfirm, PromptSelect}; +use config::get_default_era_chain_id; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use strum::IntoEnumIterator; @@ -52,9 +53,20 @@ impl EcosystemCreateArgs { // Make the only chain as a default one self.chain.set_as_default = Some(true); - let chain = - self.chain - .fill_values_with_prompt(0, &l1_network, vec![], link_to_code.clone())?; + let chains_path = PathBuf::from("chains"); + + let era_chain_id = get_default_era_chain_id(); + + let chain = self.chain.fill_values_with_prompt( + shell, + 0, + 1, + Some(l1_network), + vec![], + Some(link_to_code.clone()), + Some(chains_path), + era_chain_id, + )?; let start_containers = self.start_containers.unwrap_or_else(|| { PromptConfirm::new(MSG_START_CONTAINERS_PROMPT) diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs index 7d766d859f7c..f39ad90bbe97 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs @@ -62,7 +62,8 @@ fn create(args: EcosystemCreateArgs, shell: &Shell) -> anyhow::Result<()> { let link_to_code = resolve_link_to_code(shell, shell.current_dir(), args.link_to_code.clone())?; let spinner = Spinner::new(MSG_CREATING_INITIAL_CONFIGURATIONS_SPINNER); - let chain_config = args.chain_config(); + let mut chain_config = args.chain_config(); + chain_config.link_to_code = link_to_code.display().to_string(); let chains_path = shell.create_dir("chains")?; let default_chain_name = args.chain_args.chain_name.clone(); @@ -97,7 +98,7 @@ fn create(args: EcosystemCreateArgs, shell: &Shell) -> anyhow::Result<()> { spinner.finish(); let spinner = Spinner::new(MSG_CREATING_DEFAULT_CHAIN_SPINNER); - create_chain_inner(chain_config, &ecosystem_config, shell)?; + create_chain_inner(chain_config, shell)?; spinner.finish(); if args.start_containers {