Skip to content

Commit

Permalink
Merge branch 'aj/update/zaun-new-build' into fix/declare-v0
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohiiit authored Dec 17, 2024
2 parents daa74d9 + 5212c16 commit 3fb4a0d
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 60 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ build-contracts:
make braavos-account-cairo
make argent-contracts-starknet


artifacts-linux:
make setup-linux
make build-contracts
Expand Down
25 changes: 12 additions & 13 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ 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 :
Expand Down Expand Up @@ -100,25 +105,19 @@ RUST_LOG=info cargo run -- --dev

### Contract Descriptions 🗒️

| Contract | Source Link | Local Path |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| ERC20 (starkgate) | <https://github.com/starknet-io/starkgate-contracts/blob/cairo-1/src/cairo/strk/erc20_lockable.cairo> | [src/contracts/erc20.sierra.json](./src/contracts/erc20.sierra.json) |
| ERC20 (legacy : starknet) | <https://sepolia.starkscan.co/class/0x01b661756bf7d16210fc611626e1af4569baa1781ffc964bd018f4585ae241c1> | [src/contracts/erc20.json](./src/contracts/erc20.json) |
| OpenZeppelinAccount (legacy : starknet) | <https://sepolia.starkscan.co/class/0x05c478ee27f2112411f86f207605b2e2c58cdb647bac0df27f660ef2252359c6> | [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) | <https://sepolia.starkscan.co/class/0x00d0e183745e9dae3e4e78a8ffedcce0903fc4900beace4e0abf192d4c202da3> | [src/contracts/proxy_legacy.json](./src/contracts/proxy_legacy.json) |
| ETH token bridge (legacy : starkgate) | <https://github.com/starknet-io/starkgate-contracts/blob/update-cairo-0.9.0/src/starkware/starknet/apps/starkgate/cairo/token_bridge.cairo> | [src/contracts/legacy_token_bridge.json](./src/contracts/legacy_token_bridge.json) |
| UDC (Universal Deployer Contract) | <https://sepolia.starkscan.co/class/0x07b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69> | [src/contracts/udc.json](./src/contracts/udc.json) |
| Contract | Source Link | Local Path |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| OpenZeppelinAccount (legacy : starknet) | <https://sepolia.starkscan.co/class/0x05c478ee27f2112411f86f207605b2e2c58cdb647bac0df27f660ef2252359c6> | [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) | <https://sepolia.starkscan.co/class/0x07b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69> | [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
Expand Down
5 changes: 0 additions & 5 deletions build-artifacts/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
56 changes: 24 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,13 +65,15 @@ pub struct CliArgs {
output_file: Option<String>,
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub enum CoreContractMode {
Production,
Dev,
}

#[derive(Serialize, Deserialize)]
// 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, Clone)]
pub struct ConfigFile {
pub eth_rpc: String,
pub eth_priv_key: String,
Expand Down Expand Up @@ -146,7 +148,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")
Expand Down Expand Up @@ -174,7 +176,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;
Expand All @@ -199,7 +201,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"),
)
Expand Down Expand Up @@ -266,7 +268,7 @@ pub struct BootstrapperOutput {
pub braavos_setup_outputs: Option<BraavosSetupOutput>,
}

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;

Expand Down Expand Up @@ -437,11 +439,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 {
// 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);
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;
Expand All @@ -460,28 +465,15 @@ 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
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 {
eth_bridge_setup_outputs: Some(eth_bridge_setup_outputs),
Expand Down
13 changes: 13 additions & 0 deletions src/setup_scripts/upgrade_eth_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpTransport>,
Expand Down
19 changes: 19 additions & 0 deletions src/setup_scripts/upgrade_l1_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
17 changes: 17 additions & 0 deletions src/setup_scripts/upgrade_l2_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpTransport>,
Expand Down
60 changes: 50 additions & 10 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,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),
Expand All @@ -44,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(())
}
Expand All @@ -56,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(
Expand All @@ -78,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(
Expand Down Expand Up @@ -189,8 +190,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(())
}
Expand All @@ -204,3 +204,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<F: Future<Output = color_eyre::Result<bool>>>(
mut cond: impl FnMut() -> F,
duration: Duration,
attempt_number: usize,
) -> color_eyre::Result<bool> {
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;
}
}

0 comments on commit 3fb4a0d

Please sign in to comment.