diff --git a/.github/workflows/ci-zk-toolbox-reusable.yml b/.github/workflows/ci-zk-toolbox-reusable.yml index 7e6ab2df5bb4..610156a591a7 100644 --- a/.github/workflows/ci-zk-toolbox-reusable.yml +++ b/.github/workflows/ci-zk-toolbox-reusable.yml @@ -104,6 +104,28 @@ jobs: --prover-db-name=zksync_prover_localhost_era \ --ignore-prerequisites --verbose + - name: Create and initialize chain + run: | + ci_run zk_inception chain create \ + --chain-name chain_rollup \ + --chain-id sequential \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode rollup \ + --base-token-address 0x0000000000000000000000000000000000000001 \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default true \ + --ignore-prerequisites + + ci_run zk_inception chain init \ + --deploy-paymaster \ + --l1-rpc-url=http://reth:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@postgres:5432 \ + --server-db-name=zksync_server_localhost_rollup \ + --prover-db-url=postgres://postgres:notsecurepassword@postgres:5432 \ + --prover-db-name=zksync_prover_localhost_rollup + - name: Run server run: | ci_run zk_inception server --ignore-prerequisites &>server.log & diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index cb2638929d05..6b48387f90d2 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -72,7 +72,7 @@ async function loadTestEnvironmentFromFile(chain: string): Promise for L1BatchCommitmentMode { pub struct ChainCreateArgs { #[arg(long)] chain_name: Option, - #[arg(value_parser = clap::value_parser ! (u32).range(1..))] - chain_id: Option, + #[clap(long, help = MSG_CHAIN_ID_HELP)] + chain_id: Option, #[clap(long, help = MSG_PROVER_MODE_HELP, value_enum)] prover_mode: Option, #[clap(long, help = MSG_WALLET_CREATION_HELP, value_enum)] @@ -67,38 +71,57 @@ impl ChainCreateArgs { self, number_of_chains: u32, l1_network: &L1Network, - ) -> ChainCreateArgsFinal { + ) -> 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_id = self.chain_id.unwrap_or_else(|| { - Prompt::new(MSG_CHAIN_ID_PROMPT) - .default(&(L2_CHAIN_ID + number_of_chains).to_string()) - .ask() - }); + let chain_id = self + .chain_id + .map(|v| match v { + ChainId::Sequential => L2_CHAIN_ID + number_of_chains, + ChainId::Id(v) => v, + }) + .unwrap_or_else(|| { + Prompt::new(MSG_CHAIN_ID_PROMPT) + .default(&(L2_CHAIN_ID + number_of_chains).to_string()) + .ask() + }); - let wallet_creation = PromptSelect::new( - MSG_WALLET_CREATION_PROMPT, - WalletCreation::iter().filter(|wallet| { - // Disable localhost wallets for external networks - if l1_network == &L1Network::Localhost { - true - } else { - wallet != &WalletCreation::Localhost - } - }), - ) - .ask(); + let wallet_creation = if let Some(wallet) = self.wallet_creation { + if wallet == WalletCreation::Localhost && *l1_network != L1Network::Localhost { + bail!(MSG_WALLET_CREATION_VALIDATOR_ERR); + } else { + wallet + } + } else { + PromptSelect::new( + MSG_WALLET_CREATION_PROMPT, + WalletCreation::iter().filter(|wallet| { + // Disable localhost wallets for external networks + if *l1_network == L1Network::Localhost { + true + } else { + *wallet != WalletCreation::Localhost + } + }), + ) + .ask() + }; - let prover_version = PromptSelect::new(MSG_PROVER_VERSION_PROMPT, ProverMode::iter()).ask(); + let prover_version = self.prover_mode.unwrap_or_else(|| { + PromptSelect::new(MSG_PROVER_VERSION_PROMPT, ProverMode::iter()).ask() + }); - let l1_batch_commit_data_generator_mode = PromptSelect::new( - MSG_L1_BATCH_COMMIT_DATA_GENERATOR_MODE_PROMPT, - L1BatchCommitmentModeInternal::iter(), - ) - .ask(); + let l1_batch_commit_data_generator_mode = + self.l1_batch_commit_data_generator_mode.unwrap_or_else(|| { + PromptSelect::new( + MSG_L1_BATCH_COMMIT_DATA_GENERATOR_MODE_PROMPT, + L1BatchCommitmentModeInternal::iter(), + ) + .ask() + }); let wallet_path: Option = if self.wallet_creation == Some(WalletCreation::InFile) { Some(self.wallet_path.unwrap_or_else(|| { @@ -114,32 +137,63 @@ impl ChainCreateArgs { None }; - let base_token_selection = - PromptSelect::new(MSG_BASE_TOKEN_SELECTION_PROMPT, BaseTokenSelection::iter()).ask(); - let base_token = match base_token_selection { - BaseTokenSelection::Eth => BaseToken::eth(), - BaseTokenSelection::Custom => { - let number_validator = |val: &String| -> Result<(), String> { - let Ok(val) = val.parse::() else { - return Err(MSG_NUMBER_VALIDATOR_NOT_ZERO_ERR.to_string()); - }; - if val == 0 { - return Err(MSG_NUMBER_VALIDATOR_GREATHER_THAN_ZERO_ERR.to_string()); + let number_validator = |val: &String| -> Result<(), String> { + let Ok(val) = val.parse::() else { + return Err(MSG_NUMBER_VALIDATOR_NOT_ZERO_ERR.to_string()); + }; + if val == 0 { + return Err(MSG_NUMBER_VALIDATOR_GREATHER_THAN_ZERO_ERR.to_string()); + } + Ok(()) + }; + + let base_token = if self.base_token_address.is_none() + && self.base_token_price_denominator.is_none() + && self.base_token_price_nominator.is_none() + { + let base_token_selection = + PromptSelect::new(MSG_BASE_TOKEN_SELECTION_PROMPT, BaseTokenSelection::iter()) + .ask(); + + match base_token_selection { + BaseTokenSelection::Eth => BaseToken::eth(), + BaseTokenSelection::Custom => { + let address = Prompt::new(MSG_BASE_TOKEN_ADDRESS_PROMPT).ask(); + let nominator = Prompt::new(MSG_BASE_TOKEN_PRICE_NOMINATOR_PROMPT) + .validate_with(number_validator) + .ask(); + let denominator = Prompt::new(MSG_BASE_TOKEN_PRICE_DENOMINATOR_PROMPT) + .validate_with(number_validator) + .ask(); + BaseToken { + address, + nominator, + denominator, } - Ok(()) - }; - let address = Prompt::new(MSG_BASE_TOKEN_ADDRESS_PROMPT).ask(); - let nominator = Prompt::new(MSG_BASE_TOKEN_PRICE_NOMINATOR_PROMPT) + } + } + } else { + let address = if let Some(address) = self.base_token_address { + H160::from_str(&address).context(MSG_BASE_TOKEN_ADDRESS_VALIDATOR_ERR)? + } else { + Prompt::new(MSG_BASE_TOKEN_ADDRESS_PROMPT).ask() + }; + + let nominator = self.base_token_price_nominator.unwrap_or_else(|| { + Prompt::new(MSG_BASE_TOKEN_PRICE_NOMINATOR_PROMPT) .validate_with(number_validator) - .ask(); - let denominator = Prompt::new(MSG_BASE_TOKEN_PRICE_DENOMINATOR_PROMPT) + .ask() + }); + let denominator = self.base_token_price_denominator.unwrap_or_else(|| { + Prompt::new(MSG_BASE_TOKEN_PRICE_DENOMINATOR_PROMPT) .validate_with(number_validator) - .ask(); - BaseToken { - address, - nominator, - denominator, - } + .ask() + }); + + BaseToken { + address, + nominator, + denominator, } }; @@ -149,7 +203,7 @@ impl ChainCreateArgs { .ask() }); - ChainCreateArgsFinal { + Ok(ChainCreateArgsFinal { chain_name, chain_id, prover_version, @@ -158,7 +212,7 @@ impl ChainCreateArgs { wallet_path, base_token, set_as_default, - } + }) } } @@ -179,3 +233,20 @@ enum BaseTokenSelection { Eth, Custom, } + +#[derive(Clone, Debug, Serialize, Deserialize)] +enum ChainId { + Sequential, + Id(u32), +} + +impl FromStr for ChainId { + type Err = String; + + fn from_str(s: &str) -> Result { + (s == "sequential") + .then_some(ChainId::Sequential) + .or_else(|| s.parse::().ok().map(ChainId::Id)) + .ok_or_else(|| MSG_CHAIN_ID_VALIDATOR_ERR.to_string()) + } +} diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/create.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/create.rs index 70f4442cca62..7e20ae449a8a 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/create.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/create.rs @@ -1,5 +1,6 @@ use std::cell::OnceCell; +use anyhow::Context; use common::{logger, spinner::Spinner}; use config::{ create_local_configs_dir, create_wallets, traits::SaveConfigWithBasePath, ChainConfig, @@ -11,8 +12,8 @@ use zksync_basic_types::L2ChainId; use crate::{ commands::chain::args::create::{ChainCreateArgs, ChainCreateArgsFinal}, messages::{ - MSG_CHAIN_CREATED, MSG_CREATING_CHAIN, MSG_CREATING_CHAIN_CONFIGURATIONS_SPINNER, - MSG_SELECTED_CONFIG, + MSG_ARGS_VALIDATOR_ERR, MSG_CHAIN_CREATED, MSG_CREATING_CHAIN, + MSG_CREATING_CHAIN_CONFIGURATIONS_SPINNER, MSG_SELECTED_CONFIG, }, }; @@ -26,10 +27,12 @@ fn create( ecosystem_config: &mut EcosystemConfig, shell: &Shell, ) -> anyhow::Result<()> { - let args = args.fill_values_with_prompt( - ecosystem_config.list_of_chains().len() as u32, - &ecosystem_config.l1_network, - ); + let args = args + .fill_values_with_prompt( + ecosystem_config.list_of_chains().len() as u32, + &ecosystem_config.l1_network, + ) + .context(MSG_ARGS_VALIDATOR_ERR)?; logger::note(MSG_SELECTED_CONFIG, logger::object_to_string(&args)); logger::info(MSG_CREATING_CHAIN); diff --git a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/args/create.rs b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/args/create.rs index 746558dd4e97..05fe43dc5e96 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/args/create.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/args/create.rs @@ -38,7 +38,10 @@ pub struct EcosystemCreateArgs { } impl EcosystemCreateArgs { - pub fn fill_values_with_prompt(mut self, shell: &Shell) -> EcosystemCreateArgsFinal { + pub fn fill_values_with_prompt( + mut self, + shell: &Shell, + ) -> anyhow::Result { let mut ecosystem_name = self .ecosystem_name .unwrap_or_else(|| Prompt::new(MSG_ECOSYSTEM_NAME_PROMPT).ask()); @@ -67,7 +70,7 @@ 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); + let chain = self.chain.fill_values_with_prompt(0, &l1_network)?; let start_containers = self.start_containers.unwrap_or_else(|| { PromptConfirm::new(MSG_START_CONTAINERS_PROMPT) @@ -75,7 +78,7 @@ impl EcosystemCreateArgs { .ask() }); - EcosystemCreateArgsFinal { + Ok(EcosystemCreateArgsFinal { ecosystem_name, l1_network, link_to_code, @@ -83,7 +86,7 @@ impl EcosystemCreateArgs { wallet_path: chain.wallet_path.clone(), chain_args: chain, start_containers, - } + }) } } diff --git a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/create.rs b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/create.rs index 2d4f7b3f25d9..f9940c8a9798 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/create.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/create.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, str::FromStr}; -use anyhow::bail; +use anyhow::{bail, Context}; use common::{git, logger, spinner::Spinner}; use config::{ create_local_configs_dir, create_wallets, get_default_era_chain_id, @@ -19,10 +19,10 @@ use crate::{ }, }, messages::{ - msg_created_ecosystem, MSG_CLONING_ERA_REPO_SPINNER, MSG_CREATING_DEFAULT_CHAIN_SPINNER, - MSG_CREATING_ECOSYSTEM, MSG_CREATING_INITIAL_CONFIGURATIONS_SPINNER, - MSG_ECOSYSTEM_ALREADY_EXISTS_ERR, MSG_ECOSYSTEM_CONFIG_INVALID_ERR, MSG_SELECTED_CONFIG, - MSG_STARTING_CONTAINERS_SPINNER, + msg_created_ecosystem, MSG_ARGS_VALIDATOR_ERR, MSG_CLONING_ERA_REPO_SPINNER, + MSG_CREATING_DEFAULT_CHAIN_SPINNER, MSG_CREATING_ECOSYSTEM, + MSG_CREATING_INITIAL_CONFIGURATIONS_SPINNER, MSG_ECOSYSTEM_ALREADY_EXISTS_ERR, + MSG_ECOSYSTEM_CONFIG_INVALID_ERR, MSG_SELECTED_CONFIG, MSG_STARTING_CONTAINERS_SPINNER, }, }; @@ -39,7 +39,9 @@ pub fn run(args: EcosystemCreateArgs, shell: &Shell) -> anyhow::Result<()> { } fn create(args: EcosystemCreateArgs, shell: &Shell) -> anyhow::Result<()> { - let args = args.fill_values_with_prompt(shell); + let args = args + .fill_values_with_prompt(shell) + .context(MSG_ARGS_VALIDATOR_ERR)?; logger::note(MSG_SELECTED_CONFIG, logger::object_to_string(&args)); logger::info(MSG_CREATING_ECOSYSTEM); diff --git a/zk_toolbox/crates/zk_inception/src/messages.rs b/zk_toolbox/crates/zk_inception/src/messages.rs index 6e93aa4d7e9b..b52d7aec2167 100644 --- a/zk_toolbox/crates/zk_inception/src/messages.rs +++ b/zk_toolbox/crates/zk_inception/src/messages.rs @@ -9,6 +9,7 @@ use ethers::{ pub(super) const MSG_SELECTED_CONFIG: &str = "Selected config"; pub(super) const MSG_CHAIN_NOT_INITIALIZED: &str = "Chain not initialized. Please create a chain first"; +pub(super) const MSG_ARGS_VALIDATOR_ERR: &str = "Invalid arguments"; /// Ecosystem create related messages pub(super) const MSG_L1_NETWORK_HELP: &str = "L1 Network"; @@ -103,6 +104,7 @@ pub(super) fn msg_chain_doesnt_exist_err(chain_name: &str, chains: &Vec) /// Chain create related messages pub(super) const MSG_PROVER_MODE_HELP: &str = "Prover options"; +pub(super) const MSG_CHAIN_ID_HELP: &str = "Chain ID"; pub(super) const MSG_WALLET_CREATION_HELP: &str = "Wallet options"; pub(super) const MSG_WALLET_PATH_HELP: &str = "Wallet path"; pub(super) const MSG_L1_COMMIT_DATA_GENERATOR_MODE_HELP: &str = "Commit data generation mode"; @@ -132,6 +134,10 @@ pub(super) const MSG_CREATING_CHAIN: &str = "Creating chain"; pub(super) const MSG_CHAIN_CREATED: &str = "Chain created successfully"; pub(super) const MSG_CREATING_CHAIN_CONFIGURATIONS_SPINNER: &str = "Creating chain configurations..."; +pub(super) const MSG_CHAIN_ID_VALIDATOR_ERR: &str = "Invalid chain id"; +pub(super) const MSG_BASE_TOKEN_ADDRESS_VALIDATOR_ERR: &str = "Invalid base token address"; +pub(super) const MSG_WALLET_CREATION_VALIDATOR_ERR: &str = + "Localhost wallet is not supported for external networks"; /// Chain genesis related messages pub(super) const MSG_L1_SECRETS_MUST_BE_PRESENTED: &str = "L1 secret must be presented";