Skip to content

Commit

Permalink
Merge pull request #228 from EspressoSystems/ma/rollup-deploy-script
Browse files Browse the repository at this point in the history
Deploy zkevm contracts to hotshot deployment
  • Loading branch information
Ancient123 authored Sep 22, 2023
2 parents 2d344de + 5dfc018 commit 5a67e00
Showing 1 changed file with 158 additions and 24 deletions.
182 changes: 158 additions & 24 deletions zkevm-contract-bindings/src/bin/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ use async_compatibility_layer::logging::{setup_backtrace, setup_logging};
use clap::Parser;
use contract_bindings::HotShot;
use ethers::{
prelude::{NonceManagerMiddleware, SignerMiddleware},
providers::{Http, Middleware, Provider},
signers::{coins_bip39::English, MnemonicBuilder, Signer},
types::Address,
utils::{get_contract_address, parse_ether},
};
use hex::{FromHex, FromHexError};
use sequencer_utils::{connect_rpc, Middleware as EthMiddleware};
use sequencer_utils::Middleware as EthMiddleware;
use serde::{Deserialize, Serialize};
use serde_with::with_prefix;
use std::path::PathBuf;
use std::{num::ParseIntError, path::PathBuf};
use std::{sync::Arc, time::Duration};
use url::Url;
use zkevm_contract_bindings::{
Expand All @@ -43,6 +45,10 @@ pub struct Options {
)]
pub mnemonic: String,

/// The account index of the deployer wallet.
#[arg(long, env = "ESPRESSO_ZKEVM_DEPLOY_ACCOUNT_INDEX", default_value = "0")]
pub account_index: u32,

/// The URL of an Ethereum JsonRPC where the contracts will be deployed.
#[arg(
long,
Expand All @@ -51,6 +57,16 @@ pub struct Options {
)]
pub provider_url: Url,

/// The address of the hotshot contract, if already deployed.
#[arg(long, env = "ESPRESSO_SEQUENCER_HOTSHOT_ADDRESS")]
pub hotshot_address: Option<Address>,

/// Whether to deploy the second zkevm-node.
///
/// If false, only the contracts for the first rollup are deployed.
#[arg(long, env = "ESPRESSO_ZKEVM_DEPLOY_ROLLUP_2", default_value = "false")]
pub deploy_rollup_2: bool,

/// Wallet address of the trusted aggregator for the first zkevm.
///
/// This needs to the address of the wallet that the zkevm aggregator
Expand Down Expand Up @@ -95,10 +111,25 @@ pub struct Options {
#[arg(
short,
long,
env = "ESPRESSO_ZKEVM_DEPLOY_OUTPUT",
env = "ESPRESSO_ZKEVM_DEPLOY_OUTPUT_PATH",
default_value = "deployment.env"
)]
pub output_path: PathBuf,

/// Output file path where deployment info will be stored.
#[arg(long, env = "ESPRESSO_ZKEVM_DEPLOY_OUTPUT_JSON")]
pub json: bool,

/// Polling interval for the RPC provider
///
/// By default the ether-rs default of 7 seconds will be used.
#[arg(
short,
long,
env = "ESPRESSO_ZKEVM_DEPLOY_POLLING_INTERVAL_MS",
value_parser = |arg: &str| -> Result<Duration, ParseIntError> { Ok(Duration::from_millis(arg.parse()?)) }
)]
pub polling_interval: Option<Duration>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -155,14 +186,14 @@ struct DeploymentOutput {

/// The second polygon zkevm.
#[serde(flatten, with = "prefix_zkevm_2")]
zkevm_2_input: ZkEvmDeploymentInput,
zkevm_2_input: Option<ZkEvmDeploymentInput>,
#[serde(flatten, with = "prefix_zkevm_2")]
zkevm_2_output: ZkEvmDeploymentOutput,
zkevm_2_output: Option<ZkEvmDeploymentOutput>,
}

impl DeploymentOutput {
fn to_dotenv(&self) -> String {
let mut dotenv = "# Deployment configuration\n".to_owned();
let mut dotenv = "# Deployment configuration (generated by deploy.rs)\n".to_owned();
let json = serde_json::to_value(self).unwrap();
for (key, val) in json.as_object().unwrap() {
dotenv = format!("{dotenv}{key}={val}\n")
Expand Down Expand Up @@ -279,18 +310,91 @@ async fn deploy_zkevm(
})
}

pub async fn connect_rpc(
provider: &Url,
mnemonic: &str,
index: u32,
chain_id: Option<u64>,
polling_interval: Option<Duration>,
) -> Option<Arc<EthMiddleware>> {
let mut provider = match Provider::try_from(provider.to_string()) {
Ok(provider) => provider,
Err(err) => {
tracing::error!("error connecting to RPC {}: {}", provider, err);
return None;
}
};
tracing::info!("Connected to RPC {}", provider.url());

if let Some(interval) = polling_interval {
provider.set_interval(interval);
}
tracing::info!("RPC Polling interval is {:?}", provider.get_interval());

let chain_id = match chain_id {
Some(id) => id,
None => match provider.get_chainid().await {
Ok(id) => id.as_u64(),
Err(err) => {
tracing::error!("error getting chain ID: {}", err);
return None;
}
},
};
tracing::info!("Chain ID is {}", chain_id);

let mnemonic = match MnemonicBuilder::<English>::default()
.phrase(mnemonic)
.index(index)
{
Ok(mnemonic) => mnemonic,
Err(err) => {
tracing::error!("error building walletE: {}", err);
return None;
}
};
let wallet = match mnemonic.build() {
Ok(wallet) => wallet,
Err(err) => {
tracing::error!("error opening wallet: {}", err);
return None;
}
};
let wallet = wallet.with_chain_id(chain_id);
let address = wallet.address();
Some(Arc::new(NonceManagerMiddleware::new(
SignerMiddleware::new(provider, wallet),
address,
)))
}

async fn deploy(opts: Options) -> Result<()> {
let mut provider = Provider::try_from(opts.provider_url.to_string())?;
provider.set_interval(Duration::from_millis(100));
let deployer = connect_rpc(&opts.provider_url, &opts.mnemonic, 0, None)
.await
.unwrap();
if let Some(interval) = opts.polling_interval {
provider.set_interval(interval);
}

let deployer = connect_rpc(
&opts.provider_url,
&opts.mnemonic,
opts.account_index,
None,
opts.polling_interval,
)
.await
.unwrap();
tracing::info!("Using deployer account {:?}", deployer.inner().address());

// Deploy the hotshot contract.
let hotshot = HotShot::deploy(deployer.clone(), ())?.send().await?;
let hotshot_address = hotshot.address();
tracing::info!("Deployed HotShot at {:?}", hotshot.address());
let hotshot_address = if opts.hotshot_address.is_none() {
tracing::info!("Deploying HotShot contract");
let hotshot = HotShot::deploy(deployer.clone(), ())?.send().await?;
tracing::info!("Deployed HotShot at {:?}", hotshot.address());
hotshot.address()
} else {
tracing::info!("Using existing HotShot contract");
opts.hotshot_address.unwrap()
};

// Deploy the contracts for the first zkevm-node.
let zkevm_1_input = ZkEvmDeploymentInput {
Expand All @@ -303,16 +407,23 @@ async fn deploy(opts: Options) -> Result<()> {
};
let zkevm_1_output = deploy_zkevm(&provider, deployer.clone(), &zkevm_1_input).await?;

// Deploy the contracts for the second zkevm-node.
let zkevm_2_input = ZkEvmDeploymentInput {
hotshot_address,
trusted_aggregator: opts.trusted_aggregator_2,
genesis_root: opts.genesis_root_2,
chain_id: 1002u64,
fork_id: 1u64,
network_name: "zkevm-two".to_string(),
let (zkevm_2_input, zkevm_2_output) = if opts.deploy_rollup_2 {
tracing::info!("Deploying second zkevm-node");
let zkevm_2_input = ZkEvmDeploymentInput {
hotshot_address,
trusted_aggregator: opts.trusted_aggregator_2,
genesis_root: opts.genesis_root_2,
chain_id: 1002u64,
fork_id: 1u64,
network_name: "zkevm-two".to_string(),
};
let zkevm_2_output = deploy_zkevm(&provider, deployer.clone(), &zkevm_2_input).await?;
(Some(zkevm_2_input), Some(zkevm_2_output))
} else {
tracing::info!("Not deploying second zkevm-node");
(None, None)
};
let zkevm_2_output = deploy_zkevm(&provider, deployer.clone(), &zkevm_2_input).await?;
// Deploy the contracts for the second zkevm-node.

// Save the output to a file.
let output = DeploymentOutput {
Expand All @@ -323,8 +434,13 @@ async fn deploy(opts: Options) -> Result<()> {
zkevm_2_output,
};

std::fs::write(&opts.output_path, output.to_dotenv())?;
let content = if opts.json {
serde_json::to_string_pretty(&output)?
} else {
output.to_dotenv()
};
tracing::info!("Wrote deployment output to {}", opts.output_path.display());
std::fs::write(&opts.output_path, content)?;

Ok(())
}
Expand All @@ -345,12 +461,30 @@ mod test {
use tempfile::NamedTempFile;

#[async_std::test]
async fn test_run_deploy_scripts() -> Result<()> {
async fn test_run_deploy_script_two_rollups() -> Result<()> {
setup_logging();
setup_backtrace();

let anvil = AnvilOptions::default().spawn().await;
let mut opts = Options::parse_from([""]);
opts.polling_interval = Some(Duration::from_millis(10));
opts.deploy_rollup_2 = true;
opts.provider_url = anvil.url();
opts.output_path = NamedTempFile::new()?.path().to_path_buf();

deploy(opts).await?;

Ok(())
}

#[async_std::test]
async fn test_run_deploy_script_one_rollup() -> Result<()> {
setup_logging();
setup_backtrace();

let anvil = AnvilOptions::default().spawn().await;
let mut opts = Options::parse_from([""]);
opts.polling_interval = Some(Duration::from_millis(10));
opts.provider_url = anvil.url();
opts.output_path = NamedTempFile::new()?.path().to_path_buf();

Expand Down

0 comments on commit 5a67e00

Please sign in to comment.