Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dapp-portal support to zk_inception #2659

Merged
merged 10 commits into from
Aug 20, 2024
10 changes: 10 additions & 0 deletions zk_toolbox/crates/common/src/docker.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use xshell::{cmd, Shell};

use crate::cmd::Cmd;
Expand All @@ -9,3 +10,12 @@ pub fn up(shell: &Shell, docker_compose_file: &str) -> anyhow::Result<()> {
pub fn down(shell: &Shell, docker_compose_file: &str) -> anyhow::Result<()> {
Ok(Cmd::new(cmd!(shell, "docker compose -f {docker_compose_file} down")).run()?)
}

pub fn run(shell: &Shell, docker_image: &str, docker_args: HashMap<String, String>) -> anyhow::Result<()> {
let mut args = vec![];
for (key, value) in docker_args {
args.push(key);
args.push(value);
}
Ok(Cmd::new(cmd!(shell, "docker run {args...} {docker_image}")).run()?)
}
2 changes: 2 additions & 0 deletions zk_toolbox/crates/zk_inception/src/commands/args/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub use containers::*;
pub use portal::*;
pub use run_server::*;
pub use update::*;

mod containers;
mod portal;
mod run_server;
mod update;
8 changes: 8 additions & 0 deletions zk_toolbox/crates/zk_inception/src/commands/args/portal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use clap::Parser;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Parser)]
pub struct PortalArgs {
#[clap(long, default_value = "3000", help = "The port number for the portal app")]
pub port: u16,
}
1 change: 1 addition & 0 deletions zk_toolbox/crates/zk_inception/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod containers;
pub mod contract_verifier;
pub mod ecosystem;
pub mod external_node;
pub mod portal;
pub mod prover;
pub mod server;
pub mod update;
240 changes: 240 additions & 0 deletions zk_toolbox/crates/zk_inception/src/commands/portal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use anyhow::{anyhow, Context};
use common::{docker, logger};
use config::{ChainConfig, EcosystemConfig};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::io::Write;
use std::fs::File;
use std::path::PathBuf;
use types::L1Network;
use xshell::Shell;

use crate::{
commands::args::PortalArgs,
consts::{L2_BASE_TOKEN_ADDRESS, PORTAL_CONFIG_FILE, PORTAL_DOCKER_IMAGE, PORTAL_DOCKER_PORT},
messages::{MSG_CHAINS_NOT_INITIALIZED, MSG_FAILED_TO_START_PORTAL_ERR, MSG_STARTING_PORTAL},
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PortalRuntimeConfig {
#[serde(rename = "nodeType")]
pub node_type: String,
#[serde(rename = "hyperchainsConfig")]
pub hyperchain_configs: HyperchainsConfigs,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HyperchainsConfigs(pub Vec<HyperchainConfig>);

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HyperchainConfig {
pub network: NetworkConfig,
pub tokens: Vec<TokenConfig>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NetworkConfig {
pub id: u64, // L2 Network ID
pub key: String, // L2 Network key
pub name: String, // L2 Network name
#[serde(rename = "rpcUrl")]
pub rpc_url: String, // L2 RPC URL
#[serde(rename = "blockExplorerUrl", skip_serializing_if = "Option::is_none")]
pub block_explorer_url: Option<String>, // L2 Block Explorer URL
#[serde(rename = "blockExplorerApi", skip_serializing_if = "Option::is_none")]
pub block_explorer_api: Option<String>, // L2 Block Explorer API
#[serde(rename = "publicL1NetworkId", skip_serializing_if = "Option::is_none")]
pub public_l1_network_id: Option<u64>, // Ethereum Mainnet or Ethereum Sepolia Testnet ID
#[serde(rename = "l1Network", skip_serializing_if = "Option::is_none")]
pub l1_network: Option<L1NetworkConfig>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct L1NetworkConfig {
pub id: u64,
pub name: String,
pub network: String,
#[serde(rename = "nativeCurrency")]
pub native_currency: NativeCurrency,
#[serde(rename = "rpcUrls")]
pub rpc_urls: RpcUrls,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NativeCurrency {
pub name: String,
pub symbol: String,
pub decimals: u8,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RpcUrls {
pub default: RpcUrlConfig,
pub public: RpcUrlConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RpcUrlConfig {
pub http: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TokenConfig {
pub address: String,
pub symbol: String,
pub decimals: u8,
#[serde(rename = "l1Address", skip_serializing_if = "Option::is_none")]
pub l1_address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved

pub fn create_hyperchain_config(
ecosystem_config: &EcosystemConfig,
chain_config: &ChainConfig
) -> anyhow::Result<HyperchainConfig> {
// Get L2 RPC URL from general config
let general_config = chain_config.get_general_config()?;
let rpc_url = &general_config
.api_config
.as_ref()
.context("api_config")?
.web3_json_rpc
.http_url;
// Check if L1 network is public
let public_l1_network_id = match chain_config.l1_network {
L1Network::Sepolia | L1Network::Mainnet => Some(chain_config.l1_network.chain_id()),
_ => None,
};
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
let l1_network = if public_l1_network_id.is_none() {
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
// Initialize non-public L1 network
let secrets_config = chain_config.get_secrets_config()?;
let l1_rpc_url = secrets_config.l1.as_ref().context("l1")?.l1_rpc_url.expose_str();
Some(L1NetworkConfig {
id: chain_config.l1_network.chain_id(),
name: chain_config.l1_network.to_string(),
network: chain_config.l1_network.to_string().to_lowercase(),
native_currency: NativeCurrency {
name: "Ether".to_string(),
symbol: "ETH".to_string(),
decimals: 18,
},
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
rpc_urls: RpcUrls {
default: RpcUrlConfig {
http: vec![l1_rpc_url.to_string()],
},
public: RpcUrlConfig {
http: vec![l1_rpc_url.to_string()],
},
},
})
} else {
None
};
// TODO: use bridge contract to derive base token
// TODO: add ERC20 tokens
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
// Tokens: add ETH token
let tokens = vec![
TokenConfig {
address: L2_BASE_TOKEN_ADDRESS.to_string(),
l1_address: Some("0x0000000000000000000000000000000000000000".to_string()),
Deniallugo marked this conversation as resolved.
Show resolved Hide resolved
symbol: "ETH".to_string(),
decimals: 18,
name: Some("Ether".to_string()),
}
];
// Build hyperchain config
Ok(HyperchainConfig {
network: NetworkConfig {
id: chain_config.chain_id.as_u64(),
key: chain_config.name.clone(),
name: chain_config.name.clone(),
rpc_url: rpc_url.to_string(),
public_l1_network_id,
l1_network,
block_explorer_url: None,
block_explorer_api: None,
},
tokens,
})
}

pub fn create_hyperchains_config(
ecosystem_config: &EcosystemConfig,
chain_configs: &[ChainConfig]
) -> anyhow::Result<HyperchainsConfigs> {
let mut hyperchain_configs = Vec::new();
for chain_config in chain_configs {
match create_hyperchain_config(ecosystem_config, chain_config) {
Ok(config) => hyperchain_configs.push(config),
Err(e) => logger::warn(format!("Failed to create HyperchainConfig: {}", e)),
}
}
// Ensure at least one HyperchainConfig is created
if hyperchain_configs.is_empty() {
Err(anyhow!("Failed to create any valid HyperchainConfig"))
} else {
Ok(HyperchainsConfigs(hyperchain_configs))
}
}

pub fn run(shell: &Shell, args: PortalArgs) -> anyhow::Result<()> {
let ecosystem_config = EcosystemConfig::from_file(shell)?;
let chains = ecosystem_config.list_of_chains();

let mut chain_configs = Vec::new();
for chain in chains {
match ecosystem_config.load_chain(Some(chain.clone())) {
Some(chain_config) => chain_configs.push(chain_config),
None => {
logger::warn(format!("Chain '{}' is not initialized, skipping", chain));
continue;
}
}
}
if chain_configs.is_empty() {
return Err(anyhow!(MSG_CHAINS_NOT_INITIALIZED));
}

let hyperchain_configs = create_hyperchains_config(&ecosystem_config, &chain_configs)?;
let runtime_config = PortalRuntimeConfig {
node_type: "hyperchain".to_string(),
hyperchain_configs,
};

// Serialize and save runtime config to file
let json = serde_json::to_string_pretty(&runtime_config)
.map_err(|e| anyhow!("Failed to serialize hyperchain config: {}", e))?;
let config_js_content = format!("window['##runtimeConfig'] = {};", json);

let config_file_path = shell.current_dir().join(PORTAL_CONFIG_FILE);
let mut file = File::create(&config_file_path)
.with_context(|| format!("Failed to create {}", PORTAL_CONFIG_FILE))?;
file.write_all(config_js_content.as_bytes())
.with_context(|| format!("Failed to write to {}", PORTAL_CONFIG_FILE))?;

logger::info(format!("Config saved to {}", config_file_path.display()));

logger::info(MSG_STARTING_PORTAL);
run_portal(shell, &config_file_path, args.port)?;
Ok(())
}

fn run_portal(
shell: &Shell,
config_file_path: &PathBuf,
port: u16,
) -> anyhow::Result<()> {
let config_file_abs_path = shell.current_dir().join(config_file_path);

let port_mapping = format!("{}:{}", port, PORTAL_DOCKER_PORT);
let volume_mapping = format!("{}:/usr/src/app/dist/config.js", config_file_abs_path.display());

let mut docker_args = HashMap::new();
docker_args.insert("-p".to_string(), port_mapping);
docker_args.insert("-v".to_string(), volume_mapping);

docker::run(shell, PORTAL_DOCKER_IMAGE, docker_args)
.with_context(|| MSG_FAILED_TO_START_PORTAL_ERR)?;
Ok(())
}
4 changes: 4 additions & 0 deletions zk_toolbox/crates/zk_inception/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ pub const PROVER_STORE_MAX_RETRIES: u16 = 10;
pub const DEFAULT_CREDENTIALS_FILE: &str = "~/.config/gcloud/application_default_credentials.json";
pub const DEFAULT_PROOF_STORE_DIR: &str = "artifacts";
pub const BELLMAN_CUDA_DIR: &str = "era-bellman-cuda";
pub const L2_BASE_TOKEN_ADDRESS: &str = "0x000000000000000000000000000000000000800A";
pub const PORTAL_CONFIG_FILE: &str = "portal.config.js";
pub const PORTAL_DOCKER_IMAGE: &str = "matterlabs/dapp-portal";
pub const PORTAL_DOCKER_PORT: u16 = 3000;
sanekmelnikov marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion zk_toolbox/crates/zk_inception/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use config::EcosystemConfig;
use xshell::Shell;

use crate::commands::{
args::RunServerArgs, chain::ChainCommands, ecosystem::EcosystemCommands,
args::{PortalArgs, RunServerArgs},
chain::ChainCommands, ecosystem::EcosystemCommands,
external_node::ExternalNodeCommands, prover::ProverCommands,
};

Expand Down Expand Up @@ -56,6 +57,8 @@ pub enum InceptionSubcommands {
/// Run contract verifier
#[command(subcommand)]
ContractVerifier(ContractVerifierCommands),
/// Run dapp-portal
Portal(PortalArgs),
/// Update zkSync
#[command(alias = "u")]
Update(UpdateArgs),
Expand Down Expand Up @@ -118,6 +121,7 @@ async fn run_subcommand(inception_args: Inception, shell: &Shell) -> anyhow::Res
InceptionSubcommands::ContractVerifier(args) => {
commands::contract_verifier::run(shell, args).await?
}
InceptionSubcommands::Portal(args) => commands::portal::run(shell, args)?,
InceptionSubcommands::Update(args) => commands::update::run(shell, args)?,
InceptionSubcommands::Markdown => {
clap_markdown::print_help_markdown::<Inception>();
Expand Down
5 changes: 5 additions & 0 deletions zk_toolbox/crates/zk_inception/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ pub(super) const MSG_STARTING_SERVER: &str = "Starting server";
pub(super) const MSG_FAILED_TO_RUN_SERVER_ERR: &str = "Failed to start server";
pub(super) const MSG_PREPARING_EN_CONFIGS: &str = "Preparing External Node config";

/// Portal related messages
pub(super) const MSG_STARTING_PORTAL: &str = "Starting portal";
pub(super) const MSG_FAILED_TO_START_PORTAL_ERR: &str = "Failed to start portal";
pub(super) const MSG_CHAINS_NOT_INITIALIZED: &str = "Chains have not been initialized";

/// Forge utils related messages
pub(super) const MSG_DEPLOYER_PK_NOT_SET_ERR: &str = "Deployer private key is not set";

Expand Down
Loading