Skip to content

Commit

Permalink
feat(katana): support Starknet mainnet rollup initialization (#3064)
Browse files Browse the repository at this point in the history
  • Loading branch information
kariy authored Feb 25, 2025
1 parent 9b93308 commit 76b2858
Show file tree
Hide file tree
Showing 6 changed files with 509 additions and 89 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion bin/katana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ katana-primitives.workspace = true
katana-rpc-types.workspace = true

anyhow.workspace = true
async-trait.workspace = true
byte-unit = "5.1.4"
cainome.workspace = true
clap.workspace = true
clap_complete.workspace = true
comfy-table = "7.1.1"
dojo-utils.workspace = true
inquire = "0.7.5"
piltover = { git = "https://github.com/keep-starknet-strange/piltover.git", rev = "fb9d988" }
piltover = { git = "https://github.com/keep-starknet-strange/piltover.git", rev = "161cb3f" }
rand.workspace = true
shellexpand = "3.1.0"
spinoff.workspace = true
Expand Down
40 changes: 16 additions & 24 deletions bin/katana/src/cli/init/deployment.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::str::FromStr;
use std::sync::Arc;

use anyhow::{anyhow, Result};
use cainome::cairo_serde;
Expand All @@ -18,14 +17,14 @@ use starknet::contract::ContractFactory;
use starknet::core::crypto::compute_hash_on_elements;
use starknet::core::types::{BlockId, BlockTag, FlattenedSierraClass, StarknetError};
use starknet::macros::short_string;
use starknet::providers::jsonrpc::HttpTransport;
use starknet::providers::{JsonRpcClient, Provider, ProviderError};
use starknet::providers::{Provider, ProviderError};
use starknet::signers::LocalWallet;
use thiserror::Error;
use tracing::trace;

type RpcProvider = Arc<JsonRpcClient<HttpTransport>>;
type InitializerAccount = SingleOwnerAccount<RpcProvider, LocalWallet>;
use super::settlement::SettlementChainProvider;

type SettlementInitializerAccount = SingleOwnerAccount<SettlementChainProvider, LocalWallet>;

/// The StarknetOS program (SNOS) is the cairo program that executes the state
/// transition of a new Katana block from the previous block.
Expand Down Expand Up @@ -62,15 +61,6 @@ const LAYOUT_BRIDGE_PROGRAM_HASH: Felt =
const BOOTLOADER_PROGRAM_HASH: Felt =
felt!("0x5ab580b04e3532b6b18f81cfa654a05e29dd8e2352d88df1e765a84072db07");

/// The contract address that handles fact verification.
///
/// This address points to Herodotus' Atlantic Fact Registry contract on Starknet Sepolia as we rely
/// on their services to generates and verifies proofs.
///
/// See on [Voyager](https://sepolia.voyager.online/contract/0x04ce7851f00b6c3289674841fd7a1b96b6fd41ed1edc248faccd672c26371b8c).
const ATLANTIC_FACT_REGISTRY_SEPOLIA: Felt =
felt!("0x4ce7851f00b6c3289674841fd7a1b96b6fd41ed1edc248faccd672c26371b8c");

#[derive(Debug)]
pub struct DeploymentOutcome {
/// The address of the deployed settlement contract.
Expand All @@ -82,7 +72,7 @@ pub struct DeploymentOutcome {
/// Deploys the settlement contract in the settlement layer and initializes it with the right
/// necessary states.
pub async fn deploy_settlement_contract(
mut account: InitializerAccount,
mut account: SettlementInitializerAccount,
chain_id: Felt,
) -> Result<DeploymentOutcome, ContractInitError> {
// This is important! Otherwise all the estimate fees after a transaction will be executed
Expand Down Expand Up @@ -114,7 +104,7 @@ pub async fn deploy_settlement_contract(
let (rpc_class, casm_hash) = prepare_contract_declaration_params(class)?;

let res = account
.declare_v2(rpc_class.into(), casm_hash)
.declare_v3(rpc_class.into(), casm_hash)
.send()
.await
.inspect(|res| {
Expand Down Expand Up @@ -147,7 +137,7 @@ pub async fn deploy_settlement_contract(
const INITIAL_BLOCK_HASH: BlockHash = Felt::ZERO;

// appchain::constructor() https://github.com/keep-starknet-strange/piltover/blob/a7d6b17f855f2295a843bfd0ab0dcd696c6229a8/src/appchain.cairo#L122-L128
let request = factory.deploy_v1(
let request = factory.deploy_v3(
vec![
// owner.
account.address(),
Expand Down Expand Up @@ -236,8 +226,9 @@ pub async fn deploy_settlement_contract(

sp.update_text("Setting fact registry...");

let facts_registry = account.provider().fact_registry();
let res = appchain
.set_facts_registry(&ATLANTIC_FACT_REGISTRY_SEPOLIA.into())
.set_facts_registry(&facts_registry.into())
.send()
.await
.inspect(|res| {
Expand Down Expand Up @@ -281,7 +272,7 @@ pub async fn deploy_settlement_contract(
pub async fn check_program_info(
chain_id: Felt,
appchain_address: Felt,
provider: &RpcProvider,
provider: &SettlementChainProvider,
) -> Result<(), ContractInitError> {
let appchain = AppchainContractReader::new(appchain_address, provider);

Expand Down Expand Up @@ -330,10 +321,11 @@ pub async fn check_program_info(
});
}

if facts_registry != ATLANTIC_FACT_REGISTRY_SEPOLIA.into() {
let expected_facts_registry = provider.fact_registry();
if facts_registry != expected_facts_registry.into() {
return Err(ContractInitError::InvalidFactRegistry {
actual: facts_registry.into(),
expected: ATLANTIC_FACT_REGISTRY_SEPOLIA,
expected: expected_facts_registry,
});
}

Expand All @@ -344,13 +336,13 @@ pub async fn check_program_info(
#[derive(Error, Debug)]
pub enum ContractInitError {
#[error("failed to declare contract: {0:#?}")]
DeclarationError(AccountError<<InitializerAccount as Account>::SignError>),
DeclarationError(AccountError<<SettlementInitializerAccount as Account>::SignError>),

#[error("failed to deploy contract: {0:#?}")]
DeploymentError(AccountError<<InitializerAccount as Account>::SignError>),
DeploymentError(AccountError<<SettlementInitializerAccount as Account>::SignError>),

#[error("failed to initialize contract: {0:#?}")]
Initialization(AccountError<<InitializerAccount as Account>::SignError>),
Initialization(AccountError<<SettlementInitializerAccount as Account>::SignError>),

#[error(
"invalid program info: layout bridge program hash mismatch - expected {expected:#x}, got \
Expand Down
61 changes: 35 additions & 26 deletions bin/katana/src/cli/init/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;

use anyhow::Context;
use clap::Args;
Expand All @@ -13,16 +12,16 @@ use katana_primitives::genesis::allocation::DevAllocationsGenerator;
use katana_primitives::genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE;
use katana_primitives::genesis::Genesis;
use katana_primitives::{ContractAddress, Felt, U256};
use prompt::CARTRIDGE_SN_SEPOLIA_PROVIDER;
use settlement::SettlementChainProvider;
use starknet::accounts::{ExecutionEncoding, SingleOwnerAccount};
use starknet::core::utils::{cairo_short_string_to_felt, parse_cairo_short_string};
use starknet::providers::jsonrpc::HttpTransport;
use starknet::providers::{JsonRpcClient, Provider};
use starknet::providers::Provider;
use starknet::signers::SigningKey;
use url::Url;

mod deployment;
mod prompt;
mod settlement;
#[cfg(feature = "init-slot")]
mod slot;

Expand Down Expand Up @@ -112,20 +111,25 @@ impl InitArgs {
let settlement_account_address = self.settlement_account.expect("must present");
let settlement_private_key = self.settlement_account_private_key.expect("must present");

let settlement_url = match settlement_chain {
SettlementChain::Sepolia => Url::parse(CARTRIDGE_SN_SEPOLIA_PROVIDER).unwrap(),
let settlement_provider = match settlement_chain {
SettlementChain::Mainnet => SettlementChainProvider::sn_mainnet(),
SettlementChain::Sepolia => SettlementChainProvider::sn_sepolia(),
#[cfg(feature = "init-custom-settlement-chain")]
SettlementChain::Custom(url) => url,
SettlementChain::Custom(url) => {
use katana_primitives::felt;

// TODO: make this configurable
let facts_registry_placeholder = felt!("0x1337");
SettlementChainProvider::new(url, facts_registry_placeholder)
}
};

let l1_provider =
Arc::new(JsonRpcClient::new(HttpTransport::new(settlement_url.clone())));
let l1_chain_id = l1_provider.chain_id().await.unwrap();
let l1_chain_id = settlement_provider.chain_id().await.unwrap();

let chain_id = cairo_short_string_to_felt(&id).unwrap();

let deployment_outcome = if let Some(contract) = self.settlement_contract {
deployment::check_program_info(chain_id, contract.into(), &l1_provider)
deployment::check_program_info(chain_id, contract.into(), &settlement_provider)
.await
.unwrap();

Expand All @@ -139,7 +143,7 @@ impl InitArgs {
// If settlement contract is not provided, then we will deploy it.
else {
let account = SingleOwnerAccount::new(
l1_provider,
settlement_provider.clone(),
SigningKey::from_secret_scalar(settlement_private_key).into(),
settlement_account_address.into(),
l1_chain_id,
Expand All @@ -152,7 +156,7 @@ impl InitArgs {
Some(Ok(Outcome {
id,
deployment_outcome,
rpc_url: settlement_url,
rpc_url: settlement_provider.url().clone(),
account: settlement_account_address,
settlement_id: parse_cairo_short_string(&l1_chain_id).unwrap(),
#[cfg(feature = "init-slot")]
Expand Down Expand Up @@ -200,8 +204,9 @@ struct SettlementChainTryFromStrError {
id: String,
}

#[derive(Debug, Clone, strum_macros::Display)]
#[derive(Debug, Clone, strum_macros::Display, PartialEq, Eq)]
enum SettlementChain {
Mainnet,
Sepolia,
#[cfg(feature = "init-custom-settlement-chain")]
Custom(Url),
Expand All @@ -215,6 +220,10 @@ impl std::str::FromStr for SettlementChain {
return Ok(SettlementChain::Sepolia);
}

if &id == "mainnet" || &id == "sn_mainnet" {
return Ok(SettlementChain::Mainnet);
}

#[cfg(feature = "init-custom-settlement-chain")]
if let Ok(url) = Url::parse(s) {
return Ok(SettlementChain::Custom(url));
Expand All @@ -234,28 +243,28 @@ impl TryFrom<&str> for SettlementChain {
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use rstest::rstest;

use super::*;

#[test]
fn sepolia_from_str() {
assert_matches!(SettlementChain::from_str("sepolia"), Ok(SettlementChain::Sepolia));
assert_matches!(SettlementChain::from_str("SEPOLIA"), Ok(SettlementChain::Sepolia));
assert_matches!(SettlementChain::from_str("sn_sepolia"), Ok(SettlementChain::Sepolia));
assert_matches!(SettlementChain::from_str("SN_SEPOLIA"), Ok(SettlementChain::Sepolia));
#[rstest]
#[case("sepolia", SettlementChain::Sepolia)]
#[case("SEPOLIA", SettlementChain::Sepolia)]
#[case("sn_sepolia", SettlementChain::Sepolia)]
#[case("SN_SEPOLIA", SettlementChain::Sepolia)]
#[case("mainnet", SettlementChain::Mainnet)]
#[case("MAINNET", SettlementChain::Mainnet)]
#[case("sn_mainnet", SettlementChain::Mainnet)]
#[case("SN_MAINNET", SettlementChain::Mainnet)]
fn test_chain_from_str(#[case] input: &str, #[case] expected: SettlementChain) {
assert_matches!(SettlementChain::from_str(input), Ok(chain) if chain == expected);
}

#[test]
fn invalid_chain() {
assert!(SettlementChain::from_str("invalid_chain").is_err());
}

#[test]
fn try_from_str() {
assert!(matches!(SettlementChain::try_from("sepolia"), Ok(SettlementChain::Sepolia)));
assert!(SettlementChain::try_from("invalid").is_err(),);
}

#[test]
#[cfg(feature = "init-custom-settlement-chain")]
fn custom_settlement_chain() {
Expand Down
Loading

0 comments on commit 76b2858

Please sign in to comment.