From 21d5b14aa982066c974b0bbb48942ee33a5d87d7 Mon Sep 17 00:00:00 2001 From: Arun Jangra Date: Wed, 11 Dec 2024 22:06:36 +0530 Subject: [PATCH 1/3] resolved comments 1 --- Makefile | 12 +++---- Readme.md | 24 ++++++-------- build-artifacts/Dockerfile | 5 --- src/main.rs | 30 ++++------------- src/setup_scripts/upgrade_eth_token.rs | 13 ++++++++ src/setup_scripts/upgrade_l1_bridge.rs | 19 +++++++++++ src/setup_scripts/upgrade_l2_bridge.rs | 17 ++++++++++ src/tests/mod.rs | 46 ++++++++++++++++++++++++-- 8 files changed, 115 insertions(+), 51 deletions(-) diff --git a/Makefile b/Makefile index b3acf8e..23283f5 100644 --- a/Makefile +++ b/Makefile @@ -97,16 +97,16 @@ argent-contracts-starknet: cp ./lib/argent-contracts-starknet/target/dev/argent_ArgentAccount.contract_class.json ./artifacts/ArgentAccount.sierra.json cp ./lib/argent-contracts-starknet/target/dev/argent_ArgentAccount.compiled_contract_class.json ./artifacts/ArgentAccount.casm.json -make artifacts-linux: - make setup-linux +make build-contracts: make starkgate-contracts-legacy make starkgate-contracts-latest make braavos-account-cairo make argent-contracts-starknet +make artifacts-linux: + make setup-linux + make build-contracts + make artifacts: make setup - make starkgate-contracts-legacy - make starkgate-contracts-latest - make braavos-account-cairo - make argent-contracts-starknet \ No newline at end of file + make build-contracts \ No newline at end of file diff --git a/Readme.md b/Readme.md index b715119..6433728 100644 --- a/Readme.md +++ b/Readme.md @@ -38,6 +38,10 @@ There are three test in the repository : - You need to comment/remove the #[ignore] tags in [src/tests/mod.rs](src/tests/mod.rs) file - Only one test can be run at one time as all the tests are e2e tests. - You also would need to restart both the chains after running each test. +- You would need to clone [Madara](https://github.com/madara-alliance/madara.git) repo by running : + ```shell + git clone --branch d188aa91efa78bcc54f92aa1035295fd50e068d2 https://github.com/madara-alliance/madara.git + ``` ```shell # 1. Run madara instance with eth as settlement layer : @@ -100,25 +104,19 @@ RUST_LOG=info cargo run -- --dev ### Contract Descriptions 🗒️ -| Contract | Source Link | Local Path | -| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| ERC20 (starkgate) | | [src/contracts/erc20.sierra.json](./src/contracts/erc20.sierra.json) | -| ERC20 (legacy : starknet) | | [src/contracts/erc20.json](./src/contracts/erc20.json) | -| OpenZeppelinAccount (legacy : starknet) | | [src/contracts/OpenZeppelinAccount.json](./src/contracts/OpenZeppelinAccount.json) | -| OpenZeppelinAccount (modified : openzeppelin) | [src/contracts/OpenZeppelinAccountCairoOne.sierra.json](src/contracts/OpenZeppelinAccountCairoOne.sierra.json) | [src/contracts/OpenZeppelinAccountCairoOne.sierra.json](./src/contracts/OpenZeppelinAccountCairoOne.sierra.json) | -| Proxy (legacy : starknet) | | [src/contracts/proxy_legacy.json](./src/contracts/proxy_legacy.json) | -| ETH token bridge (legacy : starkgate) | | [src/contracts/legacy_token_bridge.json](./src/contracts/legacy_token_bridge.json) | -| UDC (Universal Deployer Contract) | | [src/contracts/udc.json](./src/contracts/udc.json) | +| Contract | Source Link | Local Path | +| --------------------------------------------- |---------------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------------------------------- | +| OpenZeppelinAccount (legacy : starknet) | | [src/contracts/OpenZeppelinAccount.json](./src/contracts/OpenZeppelinAccount.json) | +| OpenZeppelinAccount (modified : openzeppelin) | [src/contracts/account.cairo](src/contracts/account.cairo) | [src/contracts/OpenZeppelinAccountCairoOne.sierra.json](./src/contracts/OpenZeppelinAccountCairoOne.sierra.json) | +| UDC (Universal Deployer Contract) | | [src/contracts/udc.json](./src/contracts/udc.json) | Here are some contract descriptions on why they are used in our context. -- `ERC20 (starkgate)` : This ERC20 contracts works without a proxy and is used by erc20 token bridge in - order to deploy the token on L2. -- `ERC20 (legacy : starknet)` : This contract is used for deploying the implementation of ETH token on L2. -- `ERC20 token bridge (starkgate)` : Contract for Token bridge. - `OpenZeppelinAccount (legacy : starknet)` : Contract used for declaring a temp account for declaring V1 contract that will be used to deploy the user account with provided private key in env. +- `OpenZeppelinAccount (modified : openzeppelin)` : OZ account contract modified to include `deploy_contract` + function as we deploy the UDC towards the end of the bootstrapper setup. > [!IMPORTANT] > For testing in Github CI we are using the madara binary build with diff --git a/build-artifacts/Dockerfile b/build-artifacts/Dockerfile index d908e0b..5f02ce2 100644 --- a/build-artifacts/Dockerfile +++ b/build-artifacts/Dockerfile @@ -31,11 +31,6 @@ WORKDIR /app/ RUN rm -rf build RUN ./build.sh -# Create build directory and demo file -RUN mkdir -p /app/build/Release -RUN touch /app/build/Release/demo.txt -RUN echo 'Hello, World2!' > /app/build/Release/demo.txt - # Create a simpler entrypoint script RUN echo '#!/bin/sh' > /entrypoint.sh && \ echo 'cp -r /app/build/Release/src/* /mnt/' >> /entrypoint.sh && \ diff --git a/src/main.rs b/src/main.rs index a04c508..5084a96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,8 @@ pub enum CoreContractMode { Dev, } +// TODO : There is a lot of optional stuff in the config which is needed if we run +// TODO : (continued.) individual commands. We need to think of a better design. #[derive(Serialize, Deserialize)] pub struct ConfigFile { pub eth_rpc: String, @@ -197,7 +199,7 @@ pub async fn main() { &config_file, &clients, Felt::from_str( - &config_file.udc_address.clone().expect("UDC Address not available in config. Run with SetupL2"), + &config_file.udc_address.clone().expect("UDC Address not available in config. Run with mode UDC"), ) .expect("Unable to get UDC address"), ) @@ -439,7 +441,7 @@ pub async fn setup_l2(config_file: &ConfigFile, clients: &Clients) -> Bootstrapp let account = get_account(clients, config_file).await; let core_contract_client = get_core_contract_client(config_file, clients); - println!(">>> get core contract client done"); + // setup eth bridge let eth_bridge_setup_outputs = setup_eth_bridge(Some(account.clone()), &core_contract_client, config_file, clients).await; @@ -458,28 +460,8 @@ pub async fn setup_l2(config_file: &ConfigFile, clients: &Clients) -> Bootstrapp let braavos_setup_outputs = setup_braavos(Some(account.clone()), config_file, clients, udc_setup_outputs.udc_address).await; - // upgrading the bridge : - let account = build_single_owner_account( - clients.provider_l2(), - &config_file.rollup_priv_key, - &account.address().to_hex_string(), - false, - ) - .await; - upgrade_eth_token_to_cairo_1( - &account, - clients.provider_l2(), - eth_bridge_setup_outputs.clone().l2_eth_proxy_address, - ) - .await; - upgrade_eth_bridge_to_cairo_1( - &account, - clients.provider_l2(), - eth_bridge_setup_outputs.clone().l2_eth_bridge_proxy_address, - eth_bridge_setup_outputs.clone().l2_eth_proxy_address, - ) - .await; - upgrade_l1_bridge(eth_bridge_setup_outputs.clone().l1_bridge_address, config_file).await.unwrap(); + // upgrading the eth bridge + upgrade_eth_bridge(Some(account), config_file, clients).await.expect("Unable to upgrade ETH bridge."); BootstrapperOutput { eth_bridge_setup_outputs: Some(eth_bridge_setup_outputs), diff --git a/src/setup_scripts/upgrade_eth_token.rs b/src/setup_scripts/upgrade_eth_token.rs index 8b82102..8e7c354 100644 --- a/src/setup_scripts/upgrade_eth_token.rs +++ b/src/setup_scripts/upgrade_eth_token.rs @@ -13,6 +13,19 @@ use crate::utils::constants::{ }; use crate::utils::wait_for_transaction; +/// Upgrades the Ethereum token contract implementation to Cairo 1 through a series of steps: +/// 1. Declares and deploys an ETH EIC (External Implementation Contract) +/// 2. Declares and deploys a new ETH token implementation +/// 3. Performs the upgrade process by: +/// - Adding the new implementation to the proxy +/// - Upgrading to the new implementation +/// - Registering governance and upgrade administrators +/// - Adding and replacing the new implementation class hash +/// +/// # Arguments +/// * `account` - The RPC account used to perform the transactions +/// * `rpc_provider_l2` - JSON-RPC client for L2 network communication +/// * `l2_eth_token_address` - The address of the existing ETH token contract on L2 pub async fn upgrade_eth_token_to_cairo_1( account: &RpcAccount<'_>, rpc_provider_l2: &JsonRpcClient, diff --git a/src/setup_scripts/upgrade_l1_bridge.rs b/src/setup_scripts/upgrade_l1_bridge.rs index ea33478..b6fca60 100644 --- a/src/setup_scripts/upgrade_l1_bridge.rs +++ b/src/setup_scripts/upgrade_l1_bridge.rs @@ -20,6 +20,25 @@ abigen!( abigen!(EthereumNewBridge, "artifacts/upgrade-contracts/eth_bridge_upgraded.json"); abigen!(EthereumNewBridgeEIC, "artifacts/upgrade-contracts/eic_eth_bridge.json"); +/// Upgrades the L1 Ethereum bridge implementation with a new version, including deployment of new +/// contracts and configuration of administrative roles. +/// +/// # Arguments +/// * `ethereum_bridge_address` - The address of the existing Ethereum bridge contract on L1 +/// * `config_file` - Configuration file containing network and wallet settings +/// +/// # Returns +/// * `Result<()>` - Result indicating success or failure of the upgrade process +/// +/// # Steps +/// 1. Initializes provider and wallet connections using config settings +/// 2. Deploys new bridge implementation and EIC (External Implementation Contract) +/// 3. Sets up proxy connection to existing bridge +/// 4. Performs upgrade sequence: +/// - Adds new implementation to proxy +/// - Upgrades to new implementation +/// - Registers administrative roles (app role admin, governance admin, app governor) +/// - Sets maximum total balance for ETH pub async fn upgrade_l1_bridge(ethereum_bridge_address: Address, config_file: &ConfigFile) -> color_eyre::Result<()> { let config_file = Arc::from(config_file); diff --git a/src/setup_scripts/upgrade_l2_bridge.rs b/src/setup_scripts/upgrade_l2_bridge.rs index 8fd2767..c9d7b7d 100644 --- a/src/setup_scripts/upgrade_l2_bridge.rs +++ b/src/setup_scripts/upgrade_l2_bridge.rs @@ -13,6 +13,23 @@ use crate::utils::constants::{ }; use crate::utils::wait_for_transaction; +/// Upgrades the L2 Ethereum bridge implementation to Cairo 1 through a sequence of contract +/// declarations, deployments, and configuration steps. +/// +/// # Arguments +/// * `account` - The RPC account used to perform the transactions +/// * `rpc_provider_l2` - JSON-RPC client for L2 network communication +/// * `l2_eth_bridge_address` - The address of the existing ETH bridge contract on L2 +/// * `l2_eth_token_address` - The address of the ETH token contract on L2 +/// +/// # Steps +/// 1. Declares and deploys bridge EIC (External Implementation Contract) +/// 2. Declares and deploys new bridge implementation +/// 3. Executes upgrade sequence: +/// - Adds new implementation to proxy with ETH token configuration +/// - Upgrades to new implementation +/// - Registers governance and upgrade administrators +/// - Adds and replaces implementation class hash pub async fn upgrade_eth_bridge_to_cairo_1( account: &RpcAccount<'_>, rpc_provider_l2: &JsonRpcClient, diff --git a/src/tests/mod.rs b/src/tests/mod.rs index f956e24..6afb45f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2,12 +2,13 @@ pub mod constants; mod erc20_bridge; mod eth_bridge; +use std::future::Future; use std::process::Command; use std::time::Duration; use std::{env, fs}; use rstest::rstest; -use tokio::time::sleep; +use url::Url; use crate::contract_clients::config::Clients; use crate::tests::erc20_bridge::erc20_bridge_test_helper; @@ -188,8 +189,7 @@ async fn wait_for_madara() -> color_eyre::Result<()> { env::set_current_dir("../").expect("Navigate back failed."); - // Madara build time (approx : 20 mins.) - sleep(Duration::from_secs(1200)).await; + wait_for_madara_to_be_ready(Url::parse("http://localhost:19944")?).await?; Ok(()) } @@ -203,3 +203,43 @@ fn ensure_toolchain() -> color_eyre::Result<()> { } Ok(()) } + +pub async fn wait_for_madara_to_be_ready(rpc_url: Url) -> color_eyre::Result<()> { + // We are fine with `expect` here as this function is called in the intial phases of the + // program execution + let endpoint = rpc_url.join("/health").expect("Request to health endpoint failed"); + // We would wait for about 20-25 mins for madara to be ready + wait_for_cond( + || async { + let res = reqwest::get(endpoint.clone()).await?; + res.error_for_status()?; + Ok(true) + }, + Duration::from_secs(5), + 250, + ) + .await + .expect("Could not get health of Madara"); + Ok(()) +} + +pub async fn wait_for_cond>>( + mut cond: impl FnMut() -> F, + duration: Duration, + attempt_number: usize, +) -> color_eyre::Result { + let mut attempt = 0; + loop { + let err = match cond().await { + Ok(result) => return Ok(result), + Err(err) => err, + }; + + attempt += 1; + if attempt >= attempt_number { + panic!("No answer from the node after {attempt} attempts: {:#}", err) + } + + tokio::time::sleep(duration).await; + } +} From a8c49e592bb39d20366e06296c6e632deaa54123 Mon Sep 17 00:00:00 2001 From: Arun Jangra Date: Thu, 12 Dec 2024 13:42:05 +0530 Subject: [PATCH 2/3] fix : test and bridge --- Readme.md | 7 ++++--- src/main.rs | 33 +++++++++++++++++++++++++-------- src/tests/mod.rs | 14 +++++++------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Readme.md b/Readme.md index 6433728..fdf5d1f 100644 --- a/Readme.md +++ b/Readme.md @@ -39,8 +39,9 @@ There are three test in the repository : - Only one test can be run at one time as all the tests are e2e tests. - You also would need to restart both the chains after running each test. - You would need to clone [Madara](https://github.com/madara-alliance/madara.git) repo by running : - ```shell - git clone --branch d188aa91efa78bcc54f92aa1035295fd50e068d2 https://github.com/madara-alliance/madara.git + + ```shell + git clone --branch d188aa91efa78bcc54f92aa1035295fd50e068d2 https://github.com/madara-alliance/madara.git ``` ```shell @@ -105,7 +106,7 @@ RUST_LOG=info cargo run -- --dev ### Contract Descriptions 🗒️ | Contract | Source Link | Local Path | -| --------------------------------------------- |---------------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------------------------------- | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | OpenZeppelinAccount (legacy : starknet) | | [src/contracts/OpenZeppelinAccount.json](./src/contracts/OpenZeppelinAccount.json) | | OpenZeppelinAccount (modified : openzeppelin) | [src/contracts/account.cairo](src/contracts/account.cairo) | [src/contracts/OpenZeppelinAccountCairoOne.sierra.json](./src/contracts/OpenZeppelinAccountCairoOne.sierra.json) | | UDC (Universal Deployer Contract) | | [src/contracts/udc.json](./src/contracts/udc.json) | diff --git a/src/main.rs b/src/main.rs index 5084a96..5bd0567 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use std::str::FromStr; use clap::{Parser, ValueEnum}; use contract_clients::utils::RpcAccount; use dotenv::dotenv; -use ethers::abi::Address; +use ethers::abi::{AbiEncode, Address}; use inline_colorization::*; use serde::{Deserialize, Serialize}; use setup_scripts::argent::ArgentSetupOutput; @@ -65,7 +65,7 @@ pub struct CliArgs { output_file: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub enum CoreContractMode { Production, Dev, @@ -73,7 +73,7 @@ pub enum CoreContractMode { // TODO : There is a lot of optional stuff in the config which is needed if we run // TODO : (continued.) individual commands. We need to think of a better design. -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct ConfigFile { pub eth_rpc: String, pub eth_priv_key: String, @@ -146,7 +146,7 @@ pub async fn main() { println!("{color_red}{}{color_reset}", BANNER); // Load config from file or use defaults - let config_file = match args.config { + let mut config_file = match args.config { Some(path) => { let file = File::open(path).expect("Failed to open config file"); serde_json::from_reader(file).expect("Failed to parse config file") @@ -174,7 +174,7 @@ pub async fn main() { ..Default::default() } } - BootstrapMode::SetupL2 => setup_l2(&config_file, &clients).await, + BootstrapMode::SetupL2 => setup_l2(&mut config_file, &clients).await, BootstrapMode::EthBridge => { let core_contract_client = get_core_contract_client(&config_file, &clients); let output = setup_eth_bridge(account, &core_contract_client, &config_file, &clients).await; @@ -266,7 +266,7 @@ pub struct BootstrapperOutput { pub braavos_setup_outputs: Option, } -pub async fn bootstrap(config_file: &ConfigFile, clients: &Clients) -> BootstrapperOutput { +pub async fn bootstrap(config_file: &mut ConfigFile, clients: &Clients) -> BootstrapperOutput { // setup core contract (L1) let core_contract_client = setup_core_contract(config_file, clients).await; @@ -437,8 +437,14 @@ async fn setup_braavos<'a>( braavos_setup_outputs } -pub async fn setup_l2(config_file: &ConfigFile, clients: &Clients) -> BootstrapperOutput { - let account = get_account(clients, config_file).await; +pub async fn setup_l2(config_file: &mut ConfigFile, clients: &Clients) -> BootstrapperOutput { + let address = Address::from_str("0xe7f1725e7734ce288f8367e1bb143e90bb3f0512").unwrap(); + println!(">>> address : {:?}", address.encode_hex()); + + // Had to create a temporary clone otherwise the `ConfigFile` + // will be dropped after passing into `get_account` function. + let config_file_clone = &config_file.clone(); + let account = get_account(clients, config_file_clone).await; let core_contract_client = get_core_contract_client(config_file, clients); @@ -461,6 +467,17 @@ pub async fn setup_l2(config_file: &ConfigFile, clients: &Clients) -> Bootstrapp setup_braavos(Some(account.clone()), config_file, clients, udc_setup_outputs.udc_address).await; // upgrading the eth bridge + config_file.l1_eth_bridge_address = Some(format!( + "0x{}", + eth_bridge_setup_outputs + .l1_bridge_address + .encode_hex() + .trim_start_matches("0x") + .trim_start_matches('0') + )); + config_file.l2_eth_token_proxy_address = Some(eth_bridge_setup_outputs.l2_eth_proxy_address.to_hex_string()); + config_file.l2_eth_bridge_proxy_address = + Some(eth_bridge_setup_outputs.l2_eth_bridge_proxy_address.to_hex_string()); upgrade_eth_bridge(Some(account), config_file, clients).await.expect("Unable to upgrade ETH bridge."); BootstrapperOutput { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 6afb45f..2bfbff6 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -31,7 +31,7 @@ async fn test_setup(args: &ConfigFile, clients: &Clients) -> BootstrapperOutput wait_for_madara().await.expect("Failed to start madara!"); // Setup L2 with the updated config - let l2_output = setup_l2(&config, clients).await; + let l2_output = setup_l2(&mut config, clients).await; BootstrapperOutput { starknet_contract_address: Some(core_contract_address), @@ -45,9 +45,9 @@ async fn test_setup(args: &ConfigFile, clients: &Clients) -> BootstrapperOutput #[ignore = "ignored because we have a e2e test, and this is for a local test"] async fn deploy_bridge() -> Result<(), anyhow::Error> { env_logger::init(); - let config = get_test_config_file(); + let mut config = get_test_config_file(); let clients = Clients::init_from_config(&config).await; - bootstrap(&config, &clients).await; + bootstrap(&mut config, &clients).await; Ok(()) } @@ -57,9 +57,9 @@ async fn deploy_bridge() -> Result<(), anyhow::Error> { #[ignore = "ignored because we have a e2e test, and this is for a local test"] async fn deposit_and_withdraw_eth_bridge() -> Result<(), anyhow::Error> { env_logger::init(); - let config = get_test_config_file(); + let mut config = get_test_config_file(); let clients = Clients::init_from_config(&config).await; - let out = bootstrap(&config, &clients).await; + let out = bootstrap(&mut config, &clients).await; let eth_bridge_setup = out.eth_bridge_setup_outputs.unwrap(); let _ = eth_bridge_test_helper( @@ -79,9 +79,9 @@ async fn deposit_and_withdraw_eth_bridge() -> Result<(), anyhow::Error> { #[ignore = "ignored because we have a e2e test, and this is for a local test"] async fn deposit_and_withdraw_erc20_bridge() -> Result<(), anyhow::Error> { env_logger::init(); - let config = get_test_config_file(); + let mut config = get_test_config_file(); let clients = Clients::init_from_config(&config).await; - let out = bootstrap(&config, &clients).await; + let out = bootstrap(&mut config, &clients).await; let eth_token_setup = out.erc20_bridge_setup_outputs.unwrap(); let _ = erc20_bridge_test_helper( From 5212c169ea257ec4f862d3de2db98a9909769303 Mon Sep 17 00:00:00 2001 From: Arun Jangra Date: Thu, 12 Dec 2024 13:43:23 +0530 Subject: [PATCH 3/3] feat : refactor --- src/main.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5bd0567..96fd15b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -438,9 +438,6 @@ async fn setup_braavos<'a>( } pub async fn setup_l2(config_file: &mut ConfigFile, clients: &Clients) -> BootstrapperOutput { - let address = Address::from_str("0xe7f1725e7734ce288f8367e1bb143e90bb3f0512").unwrap(); - println!(">>> address : {:?}", address.encode_hex()); - // Had to create a temporary clone otherwise the `ConfigFile` // will be dropped after passing into `get_account` function. let config_file_clone = &config_file.clone(); @@ -469,11 +466,7 @@ pub async fn setup_l2(config_file: &mut ConfigFile, clients: &Clients) -> Bootst // upgrading the eth bridge config_file.l1_eth_bridge_address = Some(format!( "0x{}", - eth_bridge_setup_outputs - .l1_bridge_address - .encode_hex() - .trim_start_matches("0x") - .trim_start_matches('0') + eth_bridge_setup_outputs.l1_bridge_address.encode_hex().trim_start_matches("0x").trim_start_matches('0') )); config_file.l2_eth_token_proxy_address = Some(eth_bridge_setup_outputs.l2_eth_proxy_address.to_hex_string()); config_file.l2_eth_bridge_proxy_address =