diff --git a/.cargo/config.toml b/.cargo/config.toml index e69de29..2821e96 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -0,0 +1,44 @@ +# [source.crates-io] +# replace-with = "vendored-sources" + +# [source."git+https://github.com/ametel01/eth-rlp-verify.git"] +# git = "https://github.com/ametel01/eth-rlp-verify.git" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/ametel01/garaga.git"] +# git = "https://github.com/ametel01/garaga.git" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/ametel01/rust-accumulators.git?branch=feat/sha2-hasher"] +# git = "https://github.com/ametel01/rust-accumulators.git" +# branch = "feat/sha2-hasher" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/lambdaclass/lambdaworks.git"] +# git = "https://github.com/lambdaclass/lambdaworks.git" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/risc0/RustCrypto-hashes.git?tag=sha2-v0.10.8-risczero.0"] +# git = "https://github.com/risc0/RustCrypto-hashes.git" +# tag = "sha2-v0.10.8-risczero.0" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/risc0/risc0-ethereum?tag=v1.1.4"] +# git = "https://github.com/risc0/risc0-ethereum" +# tag = "v1.1.4" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master"] +# git = "https://github.com/xJonathanLEI/starknet-rs.git" +# branch = "master" +# replace-with = "vendored-sources" + +# [source."git+https://github.com/OpenZeppelin/openzeppelin-contracts.git"] +# git = "https://github.com/OpenZeppelin/openzeppelin-contracts.git" +# replace-with = "vendored-sources" + +# [source.vendored-sources] +# directory = "vendor" + +# [net] +# git-fetch-with-cli = true diff --git a/.dockerignore b/.dockerignore index 399ed94..8a0a86d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,68 @@ +# Build artifacts target/ -Dockerfile -.dockerignore \ No newline at end of file +/target/ +dist/ + +# Docker files +Dockerfile* +.dockerignore +docker/ + +# Version control +.git/ +.gitignore +.github/ + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store + +# Debug files +*.log +*.log.* +debug/ + +# Development configs +.env* +*.local +.editorconfig +.eslintrc* +.prettierrc* + +# Documentation +docs/ +*.md +LICENSE + +# Test files +tests/ +*_test.go +*.test +coverage/ + +# Dependencies +node_modules/ + +# Temporary files +tmp/ +temp/ +*.tmp + +# Cargo specific +Cargo.lock # unless it's a binary project +.cargo/ +!vendor/ # Explicitly allow the vendor directory +!vendor/**/target/ # Explicitly allow target directories under vendor + +# Foundry specific +cache/ +out/ +broadcast/ + +# Misc development files +.husky/ +.lint* +.format* diff --git a/.github/workflows/cairo.yml b/.github/workflows/cairo.yml index 22bda50..f4e6212 100644 --- a/.github/workflows/cairo.yml +++ b/.github/workflows/cairo.yml @@ -6,7 +6,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - # - uses: foundry-rs/setup-snfoundry@v3 + with: + submodules: true - uses: software-mansion/setup-scarb@v1 with: scarb-version: "2.8.5" diff --git a/.gitignore b/.gitignore index 012000a..9fa16f1 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,12 @@ scripts/katana/deploy.log scripts/katana/katana.log *.pb + +vendor/ + +.env.local +.env.sepolia +.env.mainnet +docker/rebuild-all.sh + +scripts/deploy-contracts.bash.sh diff --git a/.gitmodules b/.gitmodules index b571bd0..1ce7c90 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = contracts/ethereum/lib/forge-std url = https://github.com/foundry-rs/forge-std.git shallow = true +[submodule "contracts/starknet/garaga"] + path = contracts/starknet/garaga + url = git@github.com:keep-starknet-strange/garaga.git diff --git a/Cargo.lock b/Cargo.lock index 02d6e24..941ec95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,7 +266,7 @@ dependencies = [ "derive_more", "once_cell", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", ] [[package]] @@ -284,7 +284,7 @@ dependencies = [ "derive_more", "once_cell", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", ] [[package]] @@ -1063,7 +1063,7 @@ dependencies = [ "blake2", "derivative", "digest 0.10.7", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", ] [[package]] @@ -1722,7 +1722,7 @@ dependencies = [ "alloy-contract 0.6.4", "dotenv", "ruint", - "starknet-crypto 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "starknet-crypto", "thiserror 2.0.4", "tracing-subscriber 0.3.19", ] @@ -2193,7 +2193,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "sha3", "thiserror 1.0.69", "uuid 0.8.2", @@ -2247,6 +2247,7 @@ dependencies = [ "alloy 0.6.4", "common", "tokio", + "tracing", ] [[package]] @@ -2548,8 +2549,8 @@ dependencies = [ "num-bigint", "num-traits", "pyo3", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", - "starknet-crypto 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", + "starknet-crypto", "wasm-bindgen", ] @@ -2610,7 +2611,7 @@ dependencies = [ "mmr-utils", "num-bigint", "num-traits", - "sha2 0.10.8 (git+https://github.com/risc0/RustCrypto-hashes.git?tag=sha2-v0.10.8-risczero.0)", + "sha2", "thiserror 2.0.4", "tokio", ] @@ -2688,9 +2689,9 @@ dependencies = [ "hex", "num-bigint", "num-traits", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "starknet-core", - "starknet-crypto 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "starknet-crypto", "strum", "strum_macros", "thiserror 2.0.4", @@ -3291,7 +3292,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", ] [[package]] @@ -3321,7 +3322,7 @@ checksum = "bbc2a4da0d9e52ccfe6306801a112e81a8fc0c76aa3e4449fefeda7fef72bb34" dependencies = [ "lambdaworks-math 0.10.0", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "sha3", ] @@ -3332,7 +3333,7 @@ source = "git+https://github.com/lambdaclass/lambdaworks.git#fc33967375fd4d99814 dependencies = [ "lambdaworks-math 0.11.0", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "sha3", ] @@ -4138,7 +4139,7 @@ dependencies = [ "serde", "sqlx", "starknet", - "starknet-crypto 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "starknet-crypto", "starknet-handler", "starknet-types-core", "store", @@ -4668,7 +4669,7 @@ dependencies = [ "risc0-core", "risc0-zkvm-platform", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "tracing", ] @@ -4699,7 +4700,7 @@ dependencies = [ "rrs-lib", "semver 1.0.23", "serde", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "stability", "tempfile", "tracing", @@ -4988,7 +4989,7 @@ dependencies = [ "hmac", "pbkdf2", "salsa20", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", ] [[package]] @@ -5167,17 +5168,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - [[package]] name = "sha2" version = "0.10.8" @@ -5342,7 +5332,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "smallvec", "sqlformat", "thiserror 1.0.69", @@ -5380,7 +5370,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -5424,7 +5414,7 @@ dependencies = [ "rsa", "serde", "sha1", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "smallvec", "sqlx-core", "stringprep", @@ -5462,7 +5452,7 @@ dependencies = [ "rand", "serde", "serde_json", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2", "smallvec", "sqlx-core", "stringprep", @@ -5519,7 +5509,7 @@ dependencies = [ "starknet-contract", "starknet-core", "starknet-core-derive", - "starknet-crypto 0.7.3 (git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master)", + "starknet-crypto", "starknet-macros", "starknet-providers", "starknet-signers", @@ -5533,7 +5523,7 @@ dependencies = [ "async-trait", "auto_impl", "starknet-core", - "starknet-crypto 0.7.3 (git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master)", + "starknet-crypto", "starknet-providers", "starknet-signers", "thiserror 1.0.69", @@ -5569,7 +5559,7 @@ dependencies = [ "serde_with", "sha3", "starknet-core-derive", - "starknet-crypto 0.7.3 (git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master)", + "starknet-crypto", "starknet-types-core", ] @@ -5583,25 +5573,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "starknet-crypto" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded22ccf4cb9e572ce3f77de6066af53560cd2520d508876c83bb1e6b29d5cbc" -dependencies = [ - "crypto-bigint", - "hex", - "hmac", - "num-bigint", - "num-integer", - "num-traits", - "rfc6979", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", - "starknet-curve 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "starknet-types-core", - "zeroize", -] - [[package]] name = "starknet-crypto" version = "0.7.3" @@ -5614,21 +5585,12 @@ dependencies = [ "num-integer", "num-traits", "rfc6979", - "sha2 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)", - "starknet-curve 0.5.1 (git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master)", + "sha2", + "starknet-curve", "starknet-types-core", "zeroize", ] -[[package]] -name = "starknet-curve" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcde6bd74269b8161948190ace6cf069ef20ac6e79cd2ba09b320efa7500b6de" -dependencies = [ - "starknet-types-core", -] - [[package]] name = "starknet-curve" version = "0.5.1" @@ -5644,7 +5606,7 @@ dependencies = [ "common", "crypto-bigint", "starknet", - "starknet-crypto 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "starknet-crypto", "thiserror 2.0.4", "tracing", "url", @@ -5691,7 +5653,7 @@ dependencies = [ "getrandom", "rand", "starknet-core", - "starknet-crypto 0.7.3 (git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master)", + "starknet-crypto", "thiserror 1.0.69", ] diff --git a/Cargo.toml b/Cargo.toml index f393603..3c978f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ lto = true eth-rlp-types = { git = "https://github.com/ametel01/eth-rlp-verify.git" } eth-rlp-verify = { git = "https://github.com/ametel01/eth-rlp-verify.git" } starknet = { git = "https://github.com/xJonathanLEI/starknet-rs.git", branch = "master" } +starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs.git", branch = "master" } sqlx = { version = "0.8.2", features = [ "postgres", @@ -35,6 +36,8 @@ thiserror = "2.0.4" tracing = "0.1.40" tokio = "1.41.1" dotenv = "0.15" -starknet-crypto = "0.7.3" clap = "4.5" +[patch.crates-io] +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes.git", tag = "sha2-v0.10.8-risczero.0" } +starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs.git", branch = "master" } \ No newline at end of file diff --git a/config/anvil.messaging.json b/config/anvil.messaging.json index 8b9321f..ebe3f85 100644 --- a/config/anvil.messaging.json +++ b/config/anvil.messaging.json @@ -1,9 +1,9 @@ { - "chain": "ethereum", - "rpc_url": "http://127.0.0.1:8545", - "contract_address": "0xF62eEc897fa5ef36a957702AA4a45B58fE8Fe312", - "sender_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - "private_key": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - "interval": 2, - "from_block": 21332916 -} \ No newline at end of file + "chain": "ethereum", + "rpc_url": "http://anvil:8545", + "contract_address": "0x50d1bf1Cb2873C8f32AFe3b3AA6c075b341209FE", + "sender_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "private_key": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "interval": 2, + "from_block": 21340770 +} diff --git a/contracts/ethereum/script/LocalTesting.s.sol b/contracts/ethereum/script/LocalTesting.s.sol index 8b5e950..5362952 100644 --- a/contracts/ethereum/script/LocalTesting.s.sol +++ b/contracts/ethereum/script/LocalTesting.s.sol @@ -7,14 +7,11 @@ import {L1MessageSender} from "../src/L1MessageSender.sol"; import {StarknetMessagingLocal} from "../src/StarknetMessagingLocal.sol"; -// address constant SN_CORE = 0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4; - contract LocalSetup is Script { function setUp() public {} function run() public{ - // uint256 deployerPrivateKey = vm.envUint("ACCOUNT_PRIVATE_KEY"); - uint256 deployerPrivateKey = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + uint256 deployerPrivateKey = vm.envUint("ACCOUNT_PRIVATE_KEY"); string memory json = "local_testing"; diff --git a/contracts/starknet/Scarb.lock b/contracts/starknet/Scarb.lock index ff588ad..e93d860 100644 --- a/contracts/starknet/Scarb.lock +++ b/contracts/starknet/Scarb.lock @@ -11,7 +11,6 @@ dependencies = [ [[package]] name = "garaga" version = "0.14.0" -source = "git+https://github.com/keep-starknet-strange/garaga.git?rev=5f3b232#5f3b23252a04f1714838415e69e318ed8e097c15" [[package]] name = "l1_message_proxy" diff --git a/contracts/starknet/garaga b/contracts/starknet/garaga new file mode 160000 index 0000000..5f3b232 --- /dev/null +++ b/contracts/starknet/garaga @@ -0,0 +1 @@ +Subproject commit 5f3b23252a04f1714838415e69e318ed8e097c15 diff --git a/contracts/starknet/store/src/lib.cairo b/contracts/starknet/store/src/lib.cairo index 8c0c61d..3176bb8 100644 --- a/contracts/starknet/store/src/lib.cairo +++ b/contracts/starknet/store/src/lib.cairo @@ -1,5 +1,10 @@ #[starknet::interface] pub trait IFossilStore { + fn initialize( + ref self: TContractState, + verifier_address: starknet::ContractAddress, + min_update_interval: u64 + ); fn store_latest_blockhash_from_l1(ref self: TContractState, block_number: u64, blockhash: u256); fn update_mmr_state( ref self: TContractState, @@ -34,6 +39,9 @@ mod Store { #[storage] struct Storage { + initialized: bool, + verifier_address: starknet::ContractAddress, + min_update_interval: u64, latest_blockhash_from_l1: (u64, u256), latest_mmr_block: u64, mmr_batches: Map, @@ -61,6 +69,17 @@ mod Store { #[abi(embed_v0)] impl FossilStoreImpl of super::IFossilStore { + fn initialize( + ref self: ContractState, + verifier_address: starknet::ContractAddress, + min_update_interval: u64 + ) { + assert!(!self.initialized.read(), "Contract already initialized"); + self.initialized.write(true); + self.verifier_address.write(verifier_address); + self.min_update_interval.write(min_update_interval); + } + fn store_latest_blockhash_from_l1( ref self: ContractState, block_number: u64, blockhash: u256 ) { @@ -79,6 +98,20 @@ mod Store { leaves_count: u64, mmr_root: u256, ) { + assert!( + starknet::get_caller_address() == self.verifier_address.read(), + "Only Fossil Verifier can update MMR state" + ); + + let min_update_interval = self.min_update_interval.read(); + let actual_update_interval = latest_mmr_block - self.latest_mmr_block.read(); + assert!( + actual_update_interval >= min_update_interval, + "Update interval: {} must be greater than or equal to the minimum update interval: {}", + actual_update_interval, + min_update_interval + ); + let mut curr_state = self.mmr_batches.entry(batch_index); curr_state.leaves_count.write(leaves_count); diff --git a/contracts/starknet/verifier/Scarb.toml b/contracts/starknet/verifier/Scarb.toml index 4692350..9544f16 100644 --- a/contracts/starknet/verifier/Scarb.toml +++ b/contracts/starknet/verifier/Scarb.toml @@ -6,7 +6,7 @@ edition = "2023_11" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -garaga = { git = "https://github.com/keep-starknet-strange/garaga.git", rev = "5f3b232" } +garaga = { path = "../garaga/src" } starknet.workspace = true fossil_store = { path = "../store" } @@ -22,4 +22,4 @@ fmt.workspace = true casm = true casm-add-pythonic-hints = true -[lib] +[lib] \ No newline at end of file diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 09f55c8..4e7a9f1 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -29,12 +29,15 @@ pub enum LightClientError { ConfigError(String), #[error("Polling interval must be greater than zero")] PollingIntervalError, + #[error("Chain ID is not a valid number")] + ChainIdError(#[from] std::num::ParseIntError), } pub struct LightClient { starknet_provider: StarknetProvider, l2_store_addr: Felt, verifier_addr: String, + chain_id: u64, latest_processed_block: u64, starknet_private_key: String, starknet_account_address: String, @@ -54,7 +57,7 @@ impl LightClient { let verifier_addr = get_env_var("FOSSIL_VERIFIER")?; let starknet_private_key = get_env_var("STARKNET_PRIVATE_KEY")?; let starknet_account_address = get_env_var("STARKNET_ACCOUNT_ADDRESS")?; - + let chain_id = get_env_var("CHAIN_ID")?.parse::()?; // Initialize providers let starknet_provider = StarknetProvider::new(&starknet_rpc_url)?; @@ -71,6 +74,7 @@ impl LightClient { starknet_provider, l2_store_addr, verifier_addr, + chain_id, latest_processed_block: 0, starknet_private_key, starknet_account_address, @@ -171,13 +175,6 @@ impl LightClient { .get_latest_mmr_block(&self.l2_store_addr) .await?; - info!( - latest_relayed_block, - latest_mmr_block, - num_blocks = latest_relayed_block - latest_mmr_block, - "State fetched from Starknet" - ); - // Update MMR and verify proofs self.update_mmr(latest_mmr_block, latest_relayed_block) .await?; @@ -207,6 +204,7 @@ impl LightClient { publisher::prove_mmr_update( &self.starknet_provider.rpc_url().to_string(), + self.chain_id, &self.verifier_addr, &self.starknet_private_key, &self.starknet_account_address, diff --git a/crates/ethereum/Cargo.toml b/crates/ethereum/Cargo.toml index b0b123c..da0e9e8 100644 --- a/crates/ethereum/Cargo.toml +++ b/crates/ethereum/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" common = { path = "../common" } tokio = { workspace = true } +tracing = { workspace = true } alloy = { version = "0.6.4", features = ["full", "node-bindings"] } diff --git a/crates/ethereum/src/lib.rs b/crates/ethereum/src/lib.rs index 35f0edb..46c8057 100644 --- a/crates/ethereum/src/lib.rs +++ b/crates/ethereum/src/lib.rs @@ -31,7 +31,13 @@ pub async fn get_finalized_block_hash() -> Result<(u64, String), UtilsError> { let result: Result<(u64, String), UtilsError> = async { let provider = ProviderBuilder::new() .with_recommended_fillers() - .on_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url.clone())); + .try_on_anvil_with_wallet_and_config(|anvil| anvil.fork(rpc_url.clone())) + .map_err(|e| { + UtilsError::RetryExhausted( + attempts, + format!("Failed to setup Anvil provider: {}", e), + ) + })?; let contract = BlockHashFetcher::deploy(&provider).await?; let result = contract.getBlockHash().call().await?; @@ -43,16 +49,23 @@ pub async fn get_finalized_block_hash() -> Result<(u64, String), UtilsError> { } .await; - if let Ok(value) = result { - return Ok(value); - } else { - if attempts >= MAX_RETRIES { - return Err(UtilsError::RetryExhausted( - MAX_RETRIES, - "get_finalized_block_hash".to_string(), - )); + match result { + Ok(value) => return Ok(value), + Err(e) => { + if attempts >= MAX_RETRIES { + return Err(UtilsError::RetryExhausted( + MAX_RETRIES, + format!("get_finalized_block_hash failed: {}", e), + )); + } + tracing::error!( + attempts = attempts, + max_retries = MAX_RETRIES, + error = %e.to_string(), + "Attempt failed" + ); + sleep(RETRY_DELAY).await; } - sleep(RETRY_DELAY).await; } } } diff --git a/crates/guest-mmr/Cargo.toml b/crates/guest-mmr/Cargo.toml index 95037f2..d3f76b1 100644 --- a/crates/guest-mmr/Cargo.toml +++ b/crates/guest-mmr/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" guest-types = { path = "../guest-types" } hex = "0.4" -sha2 = { git = 'https://github.com/risc0/RustCrypto-hashes.git', tag = 'sha2-v0.10.8-risczero.0', features = ['compress'] } +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes.git", tag = "sha2-v0.10.8-risczero.0", features = ['compress'] } num-bigint = "0.4.4" num-traits = "0.2.19" thiserror = "2.0.3" diff --git a/crates/guest-types/src/lib.rs b/crates/guest-types/src/lib.rs index d57bc63..83107b7 100644 --- a/crates/guest-types/src/lib.rs +++ b/crates/guest-types/src/lib.rs @@ -102,6 +102,7 @@ impl GuestOutput { // CombinedInput #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CombinedInput { + chain_id: u64, headers: Vec, mmr_input: MMRInput, skip_proof_verification: bool, @@ -109,17 +110,23 @@ pub struct CombinedInput { impl CombinedInput { pub fn new( + chain_id: u64, headers: Vec, mmr_input: MMRInput, skip_proof_verification: bool, ) -> Self { Self { + chain_id, headers, mmr_input, skip_proof_verification, } } + pub fn chain_id(&self) -> u64 { + self.chain_id + } + pub fn headers(&self) -> &Vec { &self.headers } @@ -225,19 +232,30 @@ pub struct GuestProof { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BlocksValidityInput { + chain_id: u64, headers: Vec, mmr_input: MMRInput, proofs: Vec, } impl BlocksValidityInput { - pub fn new(headers: Vec, mmr_input: MMRInput, proofs: Vec) -> Self { + pub fn new( + chain_id: u64, + headers: Vec, + mmr_input: MMRInput, + proofs: Vec, + ) -> Self { Self { + chain_id, headers, mmr_input, proofs, } } + pub fn chain_id(&self) -> u64 { + self.chain_id + } + pub fn headers(&self) -> &Vec { &self.headers } diff --git a/crates/methods/blocks-validity/src/main.rs b/crates/methods/blocks-validity/src/main.rs index b9b8bd0..f9431a9 100644 --- a/crates/methods/blocks-validity/src/main.rs +++ b/crates/methods/blocks-validity/src/main.rs @@ -9,7 +9,7 @@ fn main() { let input: BlocksValidityInput = env::read(); // Verify block headers - if !are_blocks_and_chain_valid(&input.headers()) { + if !are_blocks_and_chain_valid(&input.headers(), input.chain_id()) { env::commit(&false); } // Initialize MMR with previous state diff --git a/crates/methods/mmr-append/src/main.rs b/crates/methods/mmr-append/src/main.rs index 7e31401..8cdae23 100644 --- a/crates/methods/mmr-append/src/main.rs +++ b/crates/methods/mmr-append/src/main.rs @@ -11,7 +11,7 @@ fn main() { let input: CombinedInput = env::read(); // Verify block headers assert!( - are_blocks_and_chain_valid(&input.headers()), + are_blocks_and_chain_valid(&input.headers(), input.chain_id()), "Invalid block headers" ); // Initialize MMR with previous state diff --git a/crates/publisher/bin/build_mmr.rs b/crates/publisher/bin/build_mmr.rs index c4b94f2..a8f66d6 100644 --- a/crates/publisher/bin/build_mmr.rs +++ b/crates/publisher/bin/build_mmr.rs @@ -23,6 +23,7 @@ struct Args { async fn main() -> Result<(), Box> { initialize_logger_and_env()?; + let chain_id = get_env_var("CHAIN_ID")?.parse::()?; let rpc_url = get_env_var("STARKNET_RPC_URL")?; let verifier_address = get_env_var("FOSSIL_VERIFIER")?; let private_key = get_env_var("STARKNET_PRIVATE_KEY")?; @@ -37,6 +38,7 @@ async fn main() -> Result<(), Box> { // Initialize accumulator builder with the batch size let mut builder = AccumulatorBuilder::new( &rpc_url, + chain_id, &verifier_address, &private_key, &account_address, diff --git a/crates/publisher/bin/update_mmr.rs b/crates/publisher/bin/update_mmr.rs index a67f3c5..7cf86c5 100644 --- a/crates/publisher/bin/update_mmr.rs +++ b/crates/publisher/bin/update_mmr.rs @@ -24,6 +24,7 @@ struct Args { async fn main() -> Result<(), Box> { initialize_logger_and_env()?; + let chain_id = get_env_var("CHAIN_ID")?.parse::()?; let rpc_url = get_env_var("STARKNET_RPC_URL")?; let verifier_address = get_env_var("FOSSIL_VERIFIER")?; let private_key = get_env_var("STARKNET_PRIVATE_KEY")?; @@ -36,6 +37,7 @@ async fn main() -> Result<(), Box> { publisher::prove_mmr_update( &rpc_url, + chain_id, &verifier_address, &private_key, &account_address, diff --git a/crates/publisher/bin/verify_blocks.rs b/crates/publisher/bin/verify_blocks.rs index 2f023f9..8529e64 100644 --- a/crates/publisher/bin/verify_blocks.rs +++ b/crates/publisher/bin/verify_blocks.rs @@ -24,6 +24,7 @@ async fn main() -> Result<(), Box> { initialize_logger_and_env()?; let rpc_url = get_env_var("STARKNET_RPC_URL")?; let l2_store_address = get_env_var("FOSSIL_STORE")?; + let chain_id = get_env_var("CHAIN_ID")?.parse::()?; let args = Args::parse(); @@ -37,6 +38,7 @@ async fn main() -> Result<(), Box> { match prove_headers_integrity_and_inclusion( &rpc_url, &l2_store_address, + chain_id, &headers, Some(args.skip_proof), ) diff --git a/crates/publisher/src/api/operations.rs b/crates/publisher/src/api/operations.rs index 26b1b12..3ab6998 100644 --- a/crates/publisher/src/api/operations.rs +++ b/crates/publisher/src/api/operations.rs @@ -6,6 +6,7 @@ const DEFAULT_BATCH_SIZE: u64 = 1024; pub async fn prove_mmr_update( rpc_url: &String, + chain_id: u64, verifier_address: &String, account_private_key: &String, account_address: &String, @@ -16,6 +17,7 @@ pub async fn prove_mmr_update( ) -> Result<(), PublisherError> { let mut builder = AccumulatorBuilder::new( rpc_url, + chain_id, verifier_address, account_private_key, account_address, @@ -46,18 +48,24 @@ pub async fn prove_mmr_update( pub async fn prove_headers_integrity_and_inclusion( rpc_url: &String, l2_store_address: &String, + chain_id: u64, headers: &Vec, skip_proof_verification: Option, ) -> Result, PublisherError> { let skip_proof = skip_proof_verification.unwrap_or(false); - let validator = - ValidatorBuilder::new(rpc_url, l2_store_address, DEFAULT_BATCH_SIZE, skip_proof) - .await - .map_err(|e| { - tracing::error!(error = %e, "Failed to create ValidatorBuilder"); - e - })?; + let validator = ValidatorBuilder::new( + rpc_url, + l2_store_address, + chain_id, + DEFAULT_BATCH_SIZE, + skip_proof, + ) + .await + .map_err(|e| { + tracing::error!(error = %e, "Failed to create ValidatorBuilder"); + e + })?; let result = validator .verify_blocks_integrity_and_inclusion(headers) diff --git a/crates/publisher/src/core/accumulator.rs b/crates/publisher/src/core/accumulator.rs index cbbe5ab..ed719c3 100644 --- a/crates/publisher/src/core/accumulator.rs +++ b/crates/publisher/src/core/accumulator.rs @@ -9,6 +9,7 @@ use tracing::{debug, error, info, warn}; pub struct AccumulatorBuilder<'a> { rpc_url: &'a String, + chain_id: u64, verifier_address: &'a String, account_private_key: &'a String, account_address: &'a String, @@ -20,6 +21,7 @@ pub struct AccumulatorBuilder<'a> { impl<'a> AccumulatorBuilder<'a> { pub async fn new( rpc_url: &'a String, + chain_id: u64, verifier_address: &'a String, account_private_key: &'a String, account_address: &'a String, @@ -56,6 +58,7 @@ impl<'a> AccumulatorBuilder<'a> { Ok(Self { rpc_url, + chain_id, verifier_address, account_private_key, account_address, @@ -105,7 +108,7 @@ impl<'a> AccumulatorBuilder<'a> { let result = self .batch_processor - .process_batch(start_block, current_end) + .process_batch(self.chain_id, start_block, current_end) .await .map_err(|e| { error!( @@ -148,7 +151,7 @@ impl<'a> AccumulatorBuilder<'a> { let start_block = self.batch_processor.calculate_start_block(current_end)?; let batch_result = self .batch_processor - .process_batch(start_block, current_end) + .process_batch(self.chain_id, start_block, current_end) .await?; if let Some(result) = batch_result { @@ -193,7 +196,7 @@ impl<'a> AccumulatorBuilder<'a> { if let Some(result) = self .batch_processor - .process_batch(batch_range.start, batch_range.end) + .process_batch(self.chain_id, batch_range.start, batch_range.end) .await .map_err(|e| { error!( diff --git a/crates/publisher/src/core/batch_processor.rs b/crates/publisher/src/core/batch_processor.rs index d42a43b..3ef7370 100644 --- a/crates/publisher/src/core/batch_processor.rs +++ b/crates/publisher/src/core/batch_processor.rs @@ -6,7 +6,7 @@ use common::get_or_create_db_path; use guest_types::{CombinedInput, GuestOutput, MMRInput}; use mmr::PeaksOptions; use mmr_utils::initialize_mmr; -use tracing::{debug, error, info}; +use tracing::{debug, error, info, warn}; pub struct BatchProcessor { batch_size: u64, @@ -43,6 +43,7 @@ impl BatchProcessor { pub async fn process_batch( &self, + chain_id: u64, start_block: u64, end_block: u64, ) -> Result, AccumulatorError> { @@ -104,7 +105,7 @@ impl BatchProcessor { })?; if headers.is_empty() { - error!( + warn!( "No headers found for block range {} to {}", start_block, adjusted_end_block ); @@ -136,8 +137,12 @@ impl BatchProcessor { new_headers.clone(), ); - let combined_input = - CombinedInput::new(headers.clone(), mmr_input, self.skip_proof_verification); + let combined_input = CombinedInput::new( + chain_id, + headers.clone(), + mmr_input, + self.skip_proof_verification, + ); let proof = self .proof_generator diff --git a/crates/publisher/src/validator/validator.rs b/crates/publisher/src/validator/validator.rs index dbd2c6d..c50bf80 100644 --- a/crates/publisher/src/validator/validator.rs +++ b/crates/publisher/src/validator/validator.rs @@ -16,6 +16,7 @@ use tracing::error; pub struct ValidatorBuilder { rpc_url: String, l2_store_address: Felt, + chain_id: u64, proof_generator: ProofGenerator, batch_size: u64, skip_proof: bool, @@ -25,6 +26,7 @@ impl ValidatorBuilder { pub async fn new( rpc_url: &String, l2_store_address: &String, + chain_id: u64, batch_size: u64, skip_proof: bool, ) -> Result { @@ -40,6 +42,7 @@ impl ValidatorBuilder { Ok(Self { rpc_url: rpc_url.clone(), l2_store_address: Felt::from_hex(l2_store_address)?, + chain_id, proof_generator, batch_size, skip_proof, @@ -160,7 +163,7 @@ impl ValidatorBuilder { let mmr_input = self.prepare_mmr_input(mmr).await?; let blocks_validity_input = - BlocksValidityInput::new(batch_headers, mmr_input, guest_proofs); + BlocksValidityInput::new(self.chain_id, batch_headers, mmr_input, guest_proofs); Ok(self .proof_generator diff --git a/crates/starknet-handler/Cargo.toml b/crates/starknet-handler/Cargo.toml index 1c88c67..488e2d1 100644 --- a/crates/starknet-handler/Cargo.toml +++ b/crates/starknet-handler/Cargo.toml @@ -8,8 +8,8 @@ common = { path = "../common" } thiserror = { workspace = true } starknet = { workspace = true } -starknet-crypto = { workspace = true } tracing = { workspace = true } +starknet-crypto = { workspace = true } crypto-bigint = "0.5.5" url = "2.5.4" \ No newline at end of file diff --git a/docker-compose.build.yml b/docker-compose.build.yml new file mode 100644 index 0000000..0daee6c --- /dev/null +++ b/docker-compose.build.yml @@ -0,0 +1,15 @@ +services: + anvil: + build: + context: . + dockerfile: docker/Dockerfile.anvil + + katana: + build: + context: . + dockerfile: docker/Dockerfile.katana + + deployer: + build: + context: . + dockerfile: docker/Dockerfile.deploy \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index e70ba1a..0000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,2 +0,0 @@ -include: - - docker/compose/docker-compose.yml \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7c20599 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,113 @@ +services: + anvil: + image: fossil-anvil:latest + networks: + - fossil + ports: + - "8545:8545" + env_file: + - ${ENV_FILE:-.env.local} + healthcheck: + test: ["CMD", "cast", "block-number"] + interval: 5s + timeout: 5s + retries: 5 + + deploy-ethereum: + image: fossil-deploy:latest + networks: + - fossil + env_file: + - ${ENV_FILE:-.env.local} + volumes: + - .:/app + depends_on: + anvil: + condition: service_healthy + command: ["./scripts/deploy-ethereum.sh", "local"] + + katana: + image: fossil-katana:latest + networks: + - fossil + ports: + - "5050:5050" + volumes: + - ./config:/app/config + command: ["katana", "--messaging", "/app/config/anvil.messaging.json", "--disable-fee", "--disable-validate", "--host", "0.0.0.0"] + depends_on: + anvil: + condition: service_healthy + deploy-ethereum: + condition: service_completed_successfully + healthcheck: + test: ["CMD", "katana", "--version"] + interval: 5s + timeout: 5s + retries: 5 + + deploy-starknet: + image: fossil-deploy:latest + networks: + - fossil + env_file: + - ${ENV_FILE:-.env.local} + volumes: + - .:/app + depends_on: + katana: + condition: service_healthy + command: ["./scripts/deploy-starknet.sh", "local"] + + mmr-builder: + image: fossil-build-mmr:latest + networks: + - fossil + env_file: + - ${ENV_FILE:-.env.local} + environment: + - RUST_BACKTRACE=1 + - ANVIL_URL=http://anvil:8545 + depends_on: + deploy-starknet: + condition: service_completed_successfully + restart: on-failure:3 + + client: + image: fossil-client:latest + networks: + - fossil + env_file: + - ${ENV_FILE:-.env.local} + volumes: + - .:/app + command: ["client"] + depends_on: + deploy-starknet: + condition: service_completed_successfully + mmr-builder: + condition: service_completed_successfully + + relayer: + image: fossil-relayer:latest + networks: + - fossil + env_file: + - ${ENV_FILE:-.env.local} + volumes: + - ./scripts:/app/scripts + command: ["/bin/sh", "/app/scripts/run_relayer.sh"] + depends_on: + deploy-starknet: + condition: service_completed_successfully + mmr-builder: + condition: service_completed_successfully + healthcheck: + test: ["CMD-SHELL", "pgrep -f run_relayer.sh"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + fossil: + name: fossil-network \ No newline at end of file diff --git a/docker/Dockerfile.anvil b/docker/Dockerfile.anvil new file mode 100644 index 0000000..103f05f --- /dev/null +++ b/docker/Dockerfile.anvil @@ -0,0 +1,31 @@ +FROM ubuntu:22.04 + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install basic dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Install Foundry +RUN curl -L https://foundry.paradigm.xyz | bash +ENV PATH="/root/.foundry/bin:${PATH}" +RUN foundryup + +# Verify installation +RUN forge --version + +# Create entrypoint script +RUN echo '#!/bin/bash\n\ +if [ -z "$ETH_RPC_URL" ]; then\n\ + echo "Error: ETH_RPC_URL environment variable is not set"\n\ + exit 1\n\ +fi\n\ +anvil --fork-url "$ETH_RPC_URL" --block-time 12 --host 0.0.0.0\n\ +' > /entrypoint.sh && chmod +x /entrypoint.sh + +# Default command to run Anvil +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/docker/Dockerfile.build-mmr b/docker/Dockerfile.build-mmr new file mode 100644 index 0000000..c9e93ea --- /dev/null +++ b/docker/Dockerfile.build-mmr @@ -0,0 +1,141 @@ +# Stage 1: Build the application +FROM rust:latest AS builder + +WORKDIR /app + +# Install Foundry (includes Anvil) +RUN curl -L https://foundry.paradigm.xyz | bash && \ + /root/.foundry/bin/foundryup + +# Add foundry binaries to PATH +ENV PATH="/root/.foundry/bin:$PATH" + +# Set DATABASE_URL as build arg +ARG DATABASE_URL +ENV DATABASE_URL=${DATABASE_URL} + +# Install the RISC Zero toolchain +RUN curl -L https://risczero.com/install | bash && \ + /root/.risc0/bin/rzup install || true && \ + mkdir -p /root/.cargo/bin && \ + (ln -sf /root/.risc0/bin/cargo-risczero /root/.cargo/bin/cargo-risczero || echo "Symlink creation failed, checking directories..." && ls -la /root/.risc0/bin && ls -la /root/.cargo) + +ENV PATH="/root/.risc0/bin:/root/.cargo/bin:$PATH" + +# Step 1: Copy Cargo.toml and Cargo.lock +COPY Cargo.toml Cargo.lock ./ + +# Step 2: Copy the workspace crates first +COPY crates ./crates + +# Step 3: Create necessary directories and copy pre-fetched dependencies +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/target && \ + mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/windows && \ + mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/parallel + +# Debug the build context +RUN echo "=== Build Context ===" && \ + pwd && \ + ls -la && \ + echo "=== Vendor Directory (if exists) ===" && \ + ls -R vendor || echo "No vendor directory found!" + +# Only proceed with copy if directory exists +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/{target,windows,parallel} + +# Create the destination directory with correct permissions +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 && \ + chmod 755 /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 + +# Copy the vendor directory with permissions preserved +COPY --chown=root:root vendor/ /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/ + +# Debug and verify the copy +RUN echo "=== Debug: Directory structure after copy ===" && \ + find /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 -type d && \ + echo "=== Debug: All files in cc-1.2.2 ===" && \ + find /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2 -type f && \ + echo "=== Debug: Setting final permissions ===" && \ + chmod -R u+r,g+r /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 + +# Create cargo config for vendored dependencies +RUN mkdir -p /usr/local/cargo/registry && \ + echo '[source.crates-io]\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/risc0/RustCrypto-hashes.git"]\n\ +git = "https://github.com/risc0/RustCrypto-hashes.git"\n\ +tag = "sha2-v0.10.8-risczero.0"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/xJonathanLEI/starknet-rs.git"]\n\ +git = "https://github.com/xJonathanLEI/starknet-rs.git"\n\ +branch = "master"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/rust-accumulators.git"]\n\ +git = "https://github.com/ametel01/rust-accumulators.git"\n\ +branch = "feat/sha2-hasher"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/eth-rlp-verify.git"]\n\ +git = "https://github.com/ametel01/eth-rlp-verify.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/garaga.git"]\n\ +git = "https://github.com/ametel01/garaga.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/risc0/risc0-ethereum.git"]\n\ +git = "https://github.com/risc0/risc0-ethereum"\n\ +tag = "v1.1.4"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/lambdaclass/lambdaworks.git"]\n\ +git = "https://github.com/lambdaclass/lambdaworks.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source.vendored-sources]\n\ +directory = "/usr/local/cargo/registry/src/github.com-1ecc6299db9ec823"\n\ +\n\ +[net]\n\ +offline = true\n\ +\n\ +[patch.crates-io]\n\ +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes.git", tag = "sha2-v0.10.8-risczero.0" }' > /usr/local/cargo/config.toml + +# Step 4: Prepare sqlx before building +# RUN cargo install sqlx-cli --no-default-features --features native-tls,postgres +# RUN cargo sqlx prepare --database-url="${DATABASE_URL}" -- --package publisher --bin build-mmr + +# Step 5: Build the application +RUN cargo build --release --package publisher --bin build-mmr + +# Stage 2: Create a minimal runtime image +FROM debian:bookworm-slim + +WORKDIR /app + +# Install runtime dependencies and Foundry +RUN apt-get update && apt-get install -y \ + ca-certificates \ + openssl \ + libssl3 \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* \ + && curl -L https://foundry.paradigm.xyz | bash \ + && /root/.foundry/bin/foundryup + +# Add foundry binaries to PATH +ENV PATH="/root/.foundry/bin:$PATH" + +# Copy the compiled binary from the builder stage +COPY --from=builder /app/target/release/build-mmr . + +# Make the binary executable +RUN chmod +x /app/build-mmr + +# Define the entrypoint +ENTRYPOINT ["/app/build-mmr"] +CMD ["-n", "1"] \ No newline at end of file diff --git a/docker/Dockerfile.client b/docker/Dockerfile.client new file mode 100644 index 0000000..ecaf1bd --- /dev/null +++ b/docker/Dockerfile.client @@ -0,0 +1,121 @@ +# Stage 1: Build the application +FROM rust:latest AS builder + +WORKDIR /app + +# Install the RISC Zero toolchain +RUN curl -L https://risczero.com/install | bash && \ + /root/.risc0/bin/rzup install || true && \ + mkdir -p /root/.cargo/bin && \ + (ln -sf /root/.risc0/bin/cargo-risczero /root/.cargo/bin/cargo-risczero || echo "Symlink creation failed, checking directories..." && ls -la /root/.risc0/bin && ls -la /root/.cargo) + +ENV PATH="/root/.risc0/bin:/root/.cargo/bin:$PATH" + +# Step 1: Copy Cargo.toml and Cargo.lock +COPY Cargo.toml Cargo.lock ./ + +# Step 2: Copy the workspace crates first +COPY crates ./crates + +# Step 3: Create necessary directories and copy pre-fetched dependencies +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/target && \ + mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/windows && \ + mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/parallel + +# Debug the build context +RUN echo "=== Build Context ===" && \ + pwd && \ + ls -la && \ + echo "=== Vendor Directory (if exists) ===" && \ + ls -R vendor || echo "No vendor directory found!" + +# Only proceed with copy if directory exists +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2/src/{target,windows,parallel} + +# Create the destination directory with correct permissions +RUN mkdir -p /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 && \ + chmod 755 /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 + +# Copy the vendor directory with permissions preserved +COPY --chown=root:root vendor/ /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/ + +# Debug and verify the copy +RUN echo "=== Debug: Directory structure after copy ===" && \ + find /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 -type d && \ + echo "=== Debug: All files in cc-1.2.2 ===" && \ + find /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/cc-1.2.2 -type f && \ + echo "=== Debug: Setting final permissions ===" && \ + chmod -R u+r,g+r /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823 + +# Create cargo config for vendored dependencies +RUN mkdir -p /usr/local/cargo/registry && \ + echo '[source.crates-io]\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/risc0/RustCrypto-hashes.git"]\n\ +git = "https://github.com/risc0/RustCrypto-hashes.git"\n\ +tag = "sha2-v0.10.8-risczero.0"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/xJonathanLEI/starknet-rs.git"]\n\ +git = "https://github.com/xJonathanLEI/starknet-rs.git"\n\ +branch = "master"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/rust-accumulators.git"]\n\ +git = "https://github.com/ametel01/rust-accumulators.git"\n\ +branch = "feat/sha2-hasher"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/eth-rlp-verify.git"]\n\ +git = "https://github.com/ametel01/eth-rlp-verify.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/ametel01/garaga.git"]\n\ +git = "https://github.com/ametel01/garaga.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/risc0/risc0-ethereum.git"]\n\ +git = "https://github.com/risc0/risc0-ethereum"\n\ +tag = "v1.1.4"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source."git+https://github.com/lambdaclass/lambdaworks.git"]\n\ +git = "https://github.com/lambdaclass/lambdaworks.git"\n\ +replace-with = "vendored-sources"\n\ +\n\ +[source.vendored-sources]\n\ +directory = "/usr/local/cargo/registry/src/github.com-1ecc6299db9ec823"\n\ +\n\ +[net]\n\ +offline = true\n\ +\n\ +[patch.crates-io]\n\ +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes.git", tag = "sha2-v0.10.8-risczero.0" }' > /usr/local/cargo/config.toml + +# Step 4: Build the application +ARG DATABASE_URL +ENV DATABASE_URL=${DATABASE_URL} +RUN cargo build --release --package client + +# Stage 2: Create a minimal runtime image +FROM debian:bookworm-slim + +# Set up the working directory +WORKDIR /app + +# Install runtime dependencies with OpenSSL 3 +RUN apt-get update && apt-get install -y \ + ca-certificates \ + openssl \ + libssl3 \ + && rm -rf /var/lib/apt/lists/* + +# Copy the compiled binary from the builder stage to /usr/local/bin +COPY --from=builder /app/target/release/client /usr/local/bin/client + +# Make the binary executable +RUN chmod +x /usr/local/bin/client + +# Define the entrypoint +CMD ["client"] diff --git a/docker/Dockerfile.deploy b/docker/Dockerfile.deploy new file mode 100644 index 0000000..5a03452 --- /dev/null +++ b/docker/Dockerfile.deploy @@ -0,0 +1,37 @@ +FROM rust:slim-bookworm + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install basic dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + ca-certificates \ + pkg-config \ + libssl-dev \ + jq \ + && rm -rf /var/lib/apt/lists/* + +# Install Foundry +RUN curl -L https://foundry.paradigm.xyz | bash +ENV PATH="/root/.foundry/bin:${PATH}" +RUN foundryup + +# Set up directories and PATH for Scarb +RUN mkdir -p /root/.local/bin +ENV PATH="/root/.local/bin:${PATH}" + +# Install Scarb +RUN curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh -s -- -v 2.8.5 + +# Install Starkli +RUN curl https://get.starkli.sh | sh && \ + /root/.starkli/bin/starkliup -v 0.3.5 +ENV PATH="/root/.starkli/bin:${PATH}" + +# Create Starkli directory structure +RUN mkdir -p /root/.starkli/accounts /root/.starkli/keystores + +WORKDIR /app +CMD ["bash"] \ No newline at end of file diff --git a/docker/Dockerfile.katana b/docker/Dockerfile.katana new file mode 100644 index 0000000..f29fb20 --- /dev/null +++ b/docker/Dockerfile.katana @@ -0,0 +1,31 @@ +FROM ubuntu:22.04 + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install basic dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + ca-certificates \ + build-essential \ + pkg-config \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Dojo +RUN curl -L https://install.dojoengine.org | bash +ENV PATH="/root/.dojo/bin:${PATH}" +RUN dojoup -v 1.0.0-alpha.16 + +# Create config directory +RUN mkdir -p /app/config + +# Verify installation +RUN katana --version + +# Expose default Katana port +EXPOSE 5050 + +# Default command to run Katana +CMD ["katana", "--messaging", "/app/config/anvil.messaging.json", "--disable-fee", "--disable-validate", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/docker/Dockerfile.relayer b/docker/Dockerfile.relayer new file mode 100644 index 0000000..9abb0ff --- /dev/null +++ b/docker/Dockerfile.relayer @@ -0,0 +1,60 @@ +# Stage 1: Build the application +FROM rust:alpine AS builder + +# Set up the working directory +WORKDIR /app + +# Install build dependencies including OpenSSL static libraries +RUN apk add --no-cache \ + musl-dev \ + git \ + openssl-dev \ + openssl-libs-static \ + pkgconfig \ + gcc + +# Set environment variables for static linking +ENV OPENSSL_STATIC=1 +ENV OPENSSL_LIB_DIR=/usr/lib +ENV OPENSSL_INCLUDE_DIR=/usr/include/openssl + +# Copy only necessary workspace files +COPY Cargo.toml Cargo.lock ./ +COPY vendor /usr/local/cargo/registry +COPY crates/common ./crates/common +COPY crates/relayer ./crates/relayer + +# Create cargo config for vendored dependencies +RUN mkdir -p /usr/local/cargo/registry && echo $'[source.crates-io]\nreplace-with = "vendored-sources"\n\n[source."git+https://github.com/xJonathanLEI/starknet-rs.git?branch=master"]\ngit = "https://github.com/xJonathanLEI/starknet-rs.git"\nbranch = "master"\nreplace-with = "vendored-sources"\n\n[source.vendored-sources]\ndirectory = "/usr/local/cargo/registry"\n\n[net]\ngit-fetch-with-cli = true' > /usr/local/cargo/config.toml + +# Create a minimal workspace with dependencies +RUN echo $'[workspace]\nmembers = ["crates/common", "crates/relayer"]\nresolver = "2"\n\n\ +[workspace.dependencies]\n\ +dotenv = "0.15.0"\n\ +thiserror = "1.0.50"\n\ +starknet-crypto = "0.7.3"\n\ +tracing = "0.1.40"\n\ +tokio = { version = "1.35.0", features = ["full"] }\n\ +eyre = "0.6.11"' > workspace.toml && \ + mv workspace.toml Cargo.toml + +# Build only relayer +RUN cargo build --release --package relayer + +# Stage 2: Create a minimal runtime image +FROM alpine:latest + +# Update repositories and install SSL certificates +RUN echo "https://mirror.ette.biz/alpine/v3.19/main" > /etc/apk/repositories && \ + echo "https://mirror.ette.biz/alpine/v3.19/community" >> /etc/apk/repositories && \ + apk update && \ + apk add --no-cache ca-certificates openssl + +# Copy the compiled binary from the builder stage to /usr/local/bin +COPY --from=builder /app/target/release/relayer /usr/local/bin/relayer + +# Make the binary executable +RUN chmod +x /usr/local/bin/relayer + +# Remove the ENTRYPOINT and CMD directives +# Let docker-compose handle the command diff --git a/docker/compose/docker-compose.yml b/docker/compose/docker-compose.yml deleted file mode 100644 index 6ed81dd..0000000 --- a/docker/compose/docker-compose.yml +++ /dev/null @@ -1,165 +0,0 @@ -services: - anvil: - build: - context: ../.. - dockerfile: docker/images/anvil/Dockerfile - ports: - - "8545:8545" - env_file: - - ../../.env - environment: - - ETH_RPC_URL=${ETH_RPC_URL} - - FOUNDRY_EVM_VERSION=${FOUNDRY_EVM_VERSION} - healthcheck: - test: ["CMD", - "curl", - "-X", - "POST", - "-H", - "Content-Type: application/json", - "-d", '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}', - "http://localhost:8545" - ] - interval: 10s - retries: 5 - start_period: 10s - timeout: 5s - networks: - fossil_net: - aliases: - - anvil - - katana: - build: - context: ../.. - dockerfile: docker/images/katana/Dockerfile - ports: - - "5050:5050" - volumes: - - ../../config:/app/config - environment: - - STARKNET_RPC_URL=${STARKNET_RPC_URL} - - STARKNET_PRIVATE_KEY=${STARKNET_PRIVATE_KEY} - - STARKNET_ACCOUNT_ADDRESS=${STARKNET_ACCOUNT_ADDRESS} - healthcheck: - test: ["CMD", - "curl", - "-X", - "POST", - "-H", "Content-Type: application/json", - "-d", "{\"jsonrpc\":\"2.0\",\"method\":\"starknet_getBlockWithTxHashes\",\"params\":[\"latest\"],\"id\":1}", - "http://localhost:5050" - ] - interval: 10s - retries: 5 - start_period: 20s - timeout: 5s - depends_on: - anvil: - condition: service_healthy - command: > - katana - --messaging /app/config/anvil.messaging.json - --disable-fee - --disable-validate - --host 0.0.0.0 - networks: - fossil_net: - aliases: - - katana - - ethereum: - build: - context: ../.. - dockerfile: docker/images/ethereum/Dockerfile - env_file: - - ../../.env - environment: - - FOUNDRY_EVM_VERSION=${FOUNDRY_EVM_VERSION} - - ETH_RPC_URL=http://anvil:8545 - - PRIVATE_KEY=${ACCOUNT_PRIVATE_KEY} - volumes: - - type: bind - source: ../../contracts - target: /app/contracts - - type: bind - source: ../../scripts - target: /app/scripts - - type: bind - source: ../../contracts/ethereum/lib - target: /app/lib - - type: bind - source: ../../contracts/ethereum/foundry.toml - target: /app/foundry.toml - depends_on: - anvil: - condition: service_healthy - networks: - fossil_net: - aliases: - - ethereum-deployer - - starknet: - build: - context: ../.. - dockerfile: docker/images/starknet/Dockerfile - env_file: - - ../../.env - environment: - - STARKNET_ACCOUNT=katana-0 - - STARKNET_RPC=http://katana:5050 - - STARKNET_RPC_URL=http://katana:5050 - volumes: - - ../..:/app - depends_on: - katana: - condition: service_healthy - networks: - fossil_net: - aliases: - - starknet-deployer - - client: - build: - context: ../.. - dockerfile: docker/images/client/Dockerfile - platform: linux/amd64 - image: fossil-client:latest - env_file: - - ../../.env - environment: - - STARKNET_RPC_URL=http://katana:5050 - networks: - fossil_net: - aliases: - - light-client - - relayer: - build: - context: ../.. - dockerfile: docker/images/relayer/Dockerfile - image: fossil-relayer:latest - env_file: - - ../../.env - depends_on: - - client - networks: - fossil_net: - aliases: - - relayer - -networks: - fossil_net: - driver: bridge - ipam: - driver: default - config: - - subnet: 172.20.0.0/16 - gateway: 172.20.0.1 - driver_opts: - com.docker.network.bridge.enable_icc: "true" - com.docker.network.bridge.enable_ip_masquerade: "true" - com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" - -volumes: - scarb-cache: diff --git a/docker/images/client/Dockerfile b/docker/images/client/Dockerfile deleted file mode 100644 index bd9b59f..0000000 --- a/docker/images/client/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -FROM rust:latest AS builder - -RUN apt-get update && apt-get install -y \ - pkg-config \ - libssl-dev \ - curl \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Configure git for better reliability -RUN git config --global http.postBuffer 524288000 && \ - git config --global http.lowSpeedLimit 1000 && \ - git config --global http.lowSpeedTime 60 && \ - git config --global core.compression 0 && \ - git config --global http.sslVerify false && \ - git config --global submodule.recurse true - -# Create cargo config directory and add git-fetch-with-cli config -RUN mkdir -p /usr/local/cargo/ -COPY < Cargo.toml - -# Copy only the needed crates and Cargo.lock -COPY Cargo.lock ./ -COPY crates/relayer ./crates/relayer -COPY crates/common ./crates/common - -# Build with verbose output -RUN cargo build --release -v && \ - ls -la target/release/relayer && \ - chmod +x target/release/relayer - -# Use a newer Debian version that has OpenSSL 3 -FROM debian:bookworm -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - libssl3 \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* -COPY --from=builder /app/target/release/relayer /usr/local/bin/relayer - -# Add wrapper script -RUN echo '#!/bin/sh\nwhile true; do\n relayer\n sleep 60\ndone' > /usr/local/bin/run-relayer.sh \ - && chmod +x /usr/local/bin/run-relayer.sh - -CMD ["/usr/local/bin/run-relayer.sh"] diff --git a/scripts/deploy-ethereum.sh b/scripts/deploy-ethereum.sh new file mode 100755 index 0000000..b959a67 --- /dev/null +++ b/scripts/deploy-ethereum.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +# Ensure the script stops on the first error +set -e + +# Check if environment argument is provided +if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Available environments: local, sepolia, mainnet" + exit 1 +fi + +# Validate environment argument +ENV_TYPE="$1" +case "$ENV_TYPE" in +"local" | "sepolia" | "mainnet") + ENV_FILE="/app/.env.$ENV_TYPE" + echo "Using environment: $ENV_TYPE ($ENV_FILE)" + ;; +*) + echo "Invalid environment. Must be one of: local, sepolia, mainnet" + exit 1 + ;; +esac + +# Source the appropriate environment file +source "$ENV_FILE" +export ACCOUNT_PRIVATE_KEY=${ACCOUNT_PRIVATE_KEY} + +ETHEREUM_DIR="/app/contracts/ethereum" +CONFIG_DIR="/app/config" + +# Define colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' +BOLD='\033[1m' +RED='\033[0;31m' + +# Function to update environment variables +update_env_var() { + local var_name=$1 + local var_value=$2 + + if grep -q "^$var_name=" "$ENV_FILE"; then + echo -e "${BLUE}$var_name already exists, replacing in $ENV_FILE...${NC}" + sed -i "s|^$var_name=.*|$var_name=$var_value|" "$ENV_FILE" + else + echo -e "${BLUE}Appending $var_name to $ENV_FILE...${NC}" + echo "$var_name=$var_value" >>"$ENV_FILE" + fi +} + +# Function to update JSON config +update_json_config() { + local json_file=$1 + local contract_address=$2 + + # Create temp file in the same directory to avoid permission issues + local tmp_file="${json_file}.tmp" + + if ! jq --arg addr "$contract_address" '.contract_address = $addr' "$json_file" > "$tmp_file"; then + echo -e "${RED}Failed to update JSON file${NC}" + rm -f "$tmp_file" + return 1 + fi + + if ! mv "$tmp_file" "$json_file"; then + echo -e "${RED}Failed to replace JSON file${NC}" + rm -f "$tmp_file" + return 1 + fi + + echo -e "${BLUE}Updated contract address in $json_file${NC}" +} + +# Function to deploy with retries +deploy_contracts() { + local max_attempts=3 + local attempt=1 + local wait_time=10 + + while [ $attempt -le $max_attempts ]; do + echo -e "${BLUE}${BOLD}Deploying Ethereum contracts (Attempt $attempt/$max_attempts)...${NC}" + + if forge script script/LocalTesting.s.sol:LocalSetup --broadcast --rpc-url $ANVIL_URL; then + return 0 + fi + + if [ $attempt -lt $max_attempts ]; then + echo -e "${YELLOW}Deployment failed, retrying in ${wait_time}s...${NC}" + sleep $wait_time + fi + + attempt=$((attempt + 1)) + done + + echo -e "${RED}Failed to deploy contracts after $max_attempts attempts${NC}" + return 1 +} + +# Deploy Ethereum contracts +cd "$ETHEREUM_DIR" +deploy_contracts || exit 1 + +# Read values from the JSON file and update env vars +SN_MESSAGING=$(jq -r '.snMessaging_address' logs/local_setup.json) +L1_MESSAGE_SENDER=$(jq -r '.l1MessageSender_address' logs/local_setup.json) + +# Update the environment variables +update_env_var "SN_MESSAGING" "$SN_MESSAGING" +update_env_var "L1_MESSAGE_SENDER" "$L1_MESSAGE_SENDER" + +# Update the anvil.messaging.json config +update_json_config "$CONFIG_DIR/anvil.messaging.json" "$SN_MESSAGING" + +# Source the updated environment variables +source "$ENV_FILE" + +echo -e "${BLUE}Using L1_MESSAGE_SENDER: $L1_MESSAGE_SENDER${NC}" +echo -e "${BLUE}Using SN_MESSAGING: $SN_MESSAGING${NC}" +echo -e "${GREEN}${BOLD}Ethereum deployment completed successfully!${NC}" \ No newline at end of file diff --git a/scripts/deploy-starknet.sh b/scripts/deploy-starknet.sh new file mode 100755 index 0000000..976e23d --- /dev/null +++ b/scripts/deploy-starknet.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# Ensure the script stops on the first error +set -e + +# Store the original directory (now inside container at /app) +ORIGINAL_DIR="/app" +UPDATE_INTERVAL=40 + +# Update the environment file with new addresses +update_env_var() { + local var_name=$1 + local var_value=$2 + + if grep -q "^$var_name=" "$ENV_FILE"; then + echo -e "${BLUE}$var_name already exists, replacing in $ENV_FILE...${NC}" + sed -i "s|^$var_name=.*|$var_name=$var_value|" "$ENV_FILE" + else + echo -e "${BLUE}Appending $var_name to $ENV_FILE...${NC}" + echo "$var_name=$var_value" >>"$ENV_FILE" + fi +} + +# Check if environment argument is provided +if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Available environments: local, sepolia, mainnet" + exit 1 +fi + +# Validate environment argument +ENV_TYPE="$1" +case "$ENV_TYPE" in +"local" | "sepolia" | "mainnet") + ENV_FILE="$ORIGINAL_DIR/.env.$ENV_TYPE" + echo "Using environment: $ENV_TYPE ($ENV_FILE)" + ;; +*) + echo "Invalid environment. Must be one of: local, sepolia, mainnet" + exit 1 + ;; +esac + +# Check if environment file exists +if [ ! -f "$ENV_FILE" ]; then + echo "Error: Environment file $ENV_FILE not found" + exit 1 +fi + +# Source the appropriate environment file +source "$ENV_FILE" + +STARKNET_DIR="/app/contracts/starknet" + +# Define colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color +BOLD='\033[1m' +RED='\033[0;31m' + +# Now deploy Starknet contracts +echo -e "\n${BLUE}${BOLD}Building Starknet contracts...${NC}" +cd "$STARKNET_DIR" + +scarb build + +echo -e "\n${BLUE}${BOLD}Deploying Starknet contracts...${NC}" +# Declare and deploy Fossil Store contract +echo -e "\n${YELLOW}Declaring Fossil Store contract...${NC}" +FOSSILSTORE_HASH=$(starkli declare ./target/dev/fossil_store_Store.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Class hash declared: ${BOLD}$FOSSILSTORE_HASH${NC}" +echo + +echo -e "${YELLOW}Deploying Fossil Store contract...${NC}" +FOSSILSTORE_ADDRESS=$(starkli deploy $FOSSILSTORE_HASH --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Contract address: ${BOLD}$FOSSILSTORE_ADDRESS${NC}" +echo + +# Declare and deploy Fossil L1MessageProxy contract +echo -e "${YELLOW}Declaring Fossil L1MessageProxy contract...${NC}" +L1MESSAGEPROXY_HASH=$(starkli declare ./target/dev/l1_message_proxy_L1MessageProxy.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Class hash declared: ${BOLD}$L1MESSAGEPROXY_HASH${NC}" +echo + +echo -e "${YELLOW}Deploying Fossil L1MessageProxy contract...${NC}" +L1MESSAGEPROXY_ADDRESS=$(starkli deploy $L1MESSAGEPROXY_HASH $L1_MESSAGE_SENDER $FOSSILSTORE_ADDRESS --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Contract address: ${BOLD}$L1MESSAGEPROXY_ADDRESS${NC}" +echo + +# Declare and deploy Universal ECIP contract +echo -e "${YELLOW}Declaring Universal ECIP contract...${NC}" +ECIP_HASH=$(starkli declare ./target/dev/verifier_UniversalECIP.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Class hash declared: ${BOLD}$ECIP_HASH${NC}" +echo + +# Declare and deploy Groth16 Verifier contract +echo -e "${YELLOW}Declaring Groth16 Verifier contract...${NC}" +VERIFIER_HASH=$(starkli declare ./target/dev/verifier_Risc0Groth16VerifierBN254.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Class hash declared: ${BOLD}$VERIFIER_HASH${NC}" +echo + +echo -e "${YELLOW}Deploying Groth16 Verifier contract...${NC}" +VERIFIER_ADDRESS=$(starkli deploy $VERIFIER_HASH $ECIP_HASH --salt 1 | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Contract deployed at: ${BOLD}$VERIFIER_ADDRESS${NC}" +echo + +echo -e "${YELLOW}Declaring Fossil Verifier contract...${NC}" +FOSSIL_VERIFIER_HASH=$(starkli declare ./target/dev/verifier_FossilVerifier.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Class hash declared: ${BOLD}$FOSSIL_VERIFIER_HASH${NC}" +echo + +echo -e "${YELLOW}Deploying Fossil Verifier contract...${NC}" +FOSSIL_VERIFIER_ADDRESS=$(starkli deploy $FOSSIL_VERIFIER_HASH $VERIFIER_ADDRESS $FOSSILSTORE_ADDRESS --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) +echo -e "${GREEN}Contract deployed at: ${BOLD}$FOSSIL_VERIFIER_ADDRESS${NC}" +echo + +echo -e "${YELLOW}Initializing Fossil Store contract...${NC}" +starkli invoke $FOSSILSTORE_ADDRESS initialize $FOSSIL_VERIFIER_ADDRESS $UPDATE_INTERVAL -w +echo -e "${GREEN}Fossil Store contract initialized${NC}" +echo + +echo -e "\n${GREEN}${BOLD}All contracts deployed!${NC}" + +# Update the environment file with the new addresses +update_env_var "L2_MSG_PROXY" "$L1MESSAGEPROXY_ADDRESS" +update_env_var "FOSSIL_STORE" "$FOSSILSTORE_ADDRESS" +update_env_var "STARKNET_VERIFIER" "$VERIFIER_ADDRESS" +update_env_var "FOSSIL_VERIFIER" "$FOSSIL_VERIFIER_ADDRESS" + +# Return to original directory +cd "$ORIGINAL_DIR" + +# Source the updated environment file +source "$ENV_FILE" + +sleep 5 + +echo -e "${GREEN}${BOLD}Environment variables successfully updated in $ENV_FILE${NC}" \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100755 index ccad1c9..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash - -# Ensure the script stops on the first error -set -e - -source .env - -ETHEREUM_DIR="contracts/ethereum" - -cd $ETHEREUM_DIR && forge script script/LocalTesting.s.sol:LocalSetup --broadcast --rpc-url $ANVIL_URL - -L1_MESSAGE_SENDER=0x364C7188028348566E38D762f6095741c49f492B - -# Function to wait for Katana to be ready -# wait_for_katana() { -# echo "Waiting for Katana to be ready..." -# while ! curl -s -X POST -H "Content-Type: application/json" \ -# -d '{"jsonrpc":"2.0","method":"starknet_chainId","params":[],"id":1}' \ -# http://0.0.0.0:5050 > /dev/null; do -# echo "Katana is not ready yet. Waiting..." -# sleep 5 -# done -# echo "Katana is ready!" -# } - -# # Wait for Katana to be ready -# wait_for_katana - -# # Set absolute paths -STARKNET_DIR="../starknet" - -# Now deploy Starknet contracts -echo "Deploying Starknet contracts..." -cd $STARKNET_DIR - -scarb build - -# Declare and deploy Fossil Store contract -echo "Declaring Fossil Store contract..." -FOSSILSTORE_HASH=$(starkli declare ./target/dev/fossil_store_Store.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Class hash declared: $FOSSILSTORE_HASH" - -echo "Deploying Fossil Store contract..." -FOSSILSTORE_ADDRESS=$(starkli deploy $FOSSILSTORE_HASH --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Contract address: $FOSSILSTORE_ADDRESS" - -# Declare and deploy Fossil L1MessageProxy contract -echo "Declaring Fossil L1MessageProxy contract..." -L1MESSAGEPROXY_HASH=$(starkli declare ./target/dev/l1_message_proxy_L1MessageProxy.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Class hash declared: $L1MESSAGEPROXY_HASH" - -echo "Deploying Fossil L1MessageProxy contract..." -L1MESSAGEPROXY_ADDRESS=$(starkli deploy $L1MESSAGEPROXY_HASH $L1_MESSAGE_SENDER $FOSSILSTORE_ADDRESS --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Contract address: $L1MESSAGEPROXY_ADDRESS" - -# Declare and deploy Universal ECIP contract -echo "Declaring Universal ECIP contract..." -ECIP_HASH=$(starkli declare ./target/dev/verifier_UniversalECIP.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Class hash declared: $ECIP_HASH" - -# Declare and deploy Groth16 Verifier contract -echo "Declaring Groth16 Verifier contract..." -VERIFIER_HASH=$(starkli declare ./target/dev/verifier_Risc0Groth16VerifierBN254.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Class hash declared: $VERIFIER_HASH" - -echo "Deploying Groth16 Verifier contract..." -VERIFIER_ADDRESS=$(starkli deploy $VERIFIER_HASH $ECIP_HASH --salt 1 | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Contract deployed at: $VERIFIER_ADDRESS" - -echo "Declaring Fossil Verifier contract..." -FOSSIL_VERIFIER_HASH=$(starkli declare ./target/dev/verifier_FossilVerifier.contract_class.json --compiler-version 2.8.2 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Class hash declared: $FOSSIL_VERIFIER_HASH" - -echo "Deploying Fossil Verifier contract..." -FOSSIL_VERIFIER_ADDRESS=$(starkli deploy $FOSSIL_VERIFIER_HASH $VERIFIER_ADDRESS $FOSSILSTORE_ADDRESS --salt 1 -w | grep -o '0x[a-fA-F0-9]\{64\}' | head -1) -echo "Contract deployed at: $FOSSIL_VERIFIER_ADDRESS" - -echo "All contracts deployed!" - -# # Fetch the current Ethereum block number using `cast` -# ETH_BLOCK=$(cast block-number) -# echo "Current Ethereum block number: $ETH_BLOCK" - -# # Ensure `ETH_BLOCK` is a valid number before performing arithmetic -# if [[ $ETH_BLOCK =~ ^[0-9]+$ ]]; then -# # Subtract 256 from the current block number -# ETH_BLOCK=$((ETH_BLOCK - 256)) -# echo "Updated Ethereum block number: $ETH_BLOCK" - -# # Run the Starkli command with the updated block number -# starkli invoke $FOSSILSTORE_ADDRESS update_mmr_state $ETH_BLOCK 0x0 0x0 0x0 0x0 -# echo "Updated MMR state on Starknet for testing with block number: $ETH_BLOCK" -# else -# echo "Failed to retrieve a valid block number from 'cast'." -# fi - -# Path to the .env file -ENV_FILE="../../.env" - -# Function to update or append an environment variable in the .env file -update_env_var() { - local var_name=$1 - local var_value=$2 - if grep -q "^$var_name=" "$ENV_FILE"; then - echo "$var_name already exists, replacing..." - sed -i "s|^$var_name=.*|$var_name=$var_value|" "$ENV_FILE" - else - echo "Appending $var_name to $ENV_FILE..." - echo "$var_name=$var_value" >>"$ENV_FILE" - fi -} - -# Update the .env file with the new addresses -update_env_var "L2_MSG_PROXY" "$L1MESSAGEPROXY_ADDRESS" -update_env_var "FOSSIL_STORE" "$FOSSILSTORE_ADDRESS" -update_env_var "STARKNET_VERIFIER" "$VERIFIER_ADDRESS" -update_env_var "FOSSIL_VERIFIER" "$FOSSIL_VERIFIER_ADDRESS" -pwd - -source ../../.env - -echo "Environment variables successfully updated in $ENV_FILE" diff --git a/scripts/run_local_workflow.sh b/scripts/run_local_workflow.sh deleted file mode 100755 index 3234694..0000000 --- a/scripts/run_local_workflow.sh +++ /dev/null @@ -1,69 +0,0 @@ -# Function to wait for a specific log message in a given log file -wait_for_log() { - local log_file=$1 - local search_string=$2 - - echo "Waiting for '$search_string' in $log_file..." - while ! grep -q "$search_string" "$log_file"; do - sleep 1 - done - echo "Found '$search_string' in $log_file." -} - -# Start Terminal 1: Start Anvil Ethereum Devnet -gnome-terminal -- bash -c " -cd config; -source anvil.env; -anvil --fork-url \$ETH_RPC_URL --auto-impersonate --block-time 12 | tee anvil.log; -exec bash" - -# Wait for Anvil to be ready -wait_for_log "config/anvil.log" "Listening on" - -# Start Terminal 2: Deploy L1MessageSender.sol -gnome-terminal -- bash -c " -cd contracts/ethereum; -cp ../../config/anvil.env .env; -source .env; -forge script script/LocalTesting.s.sol:LocalSetup --broadcast --rpc-url \$ANVIL_URL; -exec bash" - -# Wait for the contract deployment to finish -sleep 10 # Adjust as needed - -# Start Terminal 3: Start Katana Starknet Devnet -gnome-terminal -- bash -c " -cd scripts/katana; -source ../../config/katana.env; -katana --messaging ../../config/anvil.messaging.json --disable-fee | tee katana.log; -exec bash" - -# Wait for Katana to be ready -wait_for_log "scripts/katana/katana.log" "RPC server started" - -# Start Terminal 4: Deploy Starknet Contracts -gnome-terminal -- bash -c " -cd scripts/katana; -./deploy.sh | tee deploy.log; -exec bash" - -# Wait for the "Environment variables successfully updated" message -wait_for_log "scripts/katana/deploy.log" "Environment variables successfully updated" - -# Start Terminal 5: Run the Rust Relayer -gnome-terminal -- bash -c " -cp config/anvil.env .env -cd relayer; -cargo run; -exec bash" - -# Wait for the Rust Relayer to start -sleep 5 # Adjust as needed - -# Start Terminal 6: Run the Rust Light Client -gnome-terminal -- bash -c " -cd client; -cargo run; -exec bash" - -echo "Local testing setup complete. All services are running." diff --git a/scripts/run_relayer.sh b/scripts/run_relayer.sh index 041f863..6be3f48 100755 --- a/scripts/run_relayer.sh +++ b/scripts/run_relayer.sh @@ -1,14 +1,45 @@ -#!/bin/bash +#!/bin/sh set -e -cd crates/relayer +# Use RELAYER_INTERVAL from environment, default to 720 if not set +INTERVAL_MINUTES=${RELAYER_INTERVAL:-720} +MAX_RETRIES=3 +RETRY_DELAY=10 # seconds -while true; do - cargo run --release - echo "Waiting 10 minutes before next run..." - for ((i=10; i>0; i--)); do - echo "Next run in $i minutes..." - sleep 60 +run_with_retry() { + attempt=1 + while [ $attempt -le $MAX_RETRIES ]; do + echo "Starting relayer (attempt $attempt/$MAX_RETRIES)..." + if /usr/local/bin/relayer; then + return 0 + fi + + exit_code=$? + echo "Relayer exited with code $exit_code" + + if [ $attempt -lt $MAX_RETRIES ]; then + echo "Retrying in $RETRY_DELAY seconds..." + sleep $RETRY_DELAY + fi + attempt=$((attempt + 1)) done + + echo "Failed to run relayer after $MAX_RETRIES attempts" + return 1 +} + +while true; do + if run_with_retry; then + echo "Relayer completed successfully. Waiting $INTERVAL_MINUTES minutes before next run..." + i=$INTERVAL_MINUTES + while [ $i -gt 0 ]; do + echo "Next run in $i minutes..." + sleep 60 + i=$((i - 1)) + done + else + echo "Relayer failed all retry attempts. Exiting..." + exit 1 + fi done diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 0000000..2caf374 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +ENV_FILE=$1 +BUILD=$2 # Optional second parameter to trigger build + +if [ "$ENV_FILE" != ".env.local" ] && [ "$ENV_FILE" != ".env.sepolia" ] && [ "$ENV_FILE" != ".env.mainnet" ]; then + echo "Usage: $0 { .env.local | .env.sepolia | .env.mainnet } [build]" + exit 1 +fi + +export ENV_FILE=$ENV_FILE + +# Clean up any existing containers +docker-compose down + +if [ "$BUILD" == "build" ]; then + # Build images + echo "Building images..." + docker-compose -f docker-compose.yml -f docker-compose.build.yml build +fi + +if [ "$ENV_FILE" == ".env.local" ]; then + # Local development setup + docker-compose up -d +else + # Production setup (sepolia/mainnet) + docker-compose up -d client relayer +fi