diff --git a/Cargo.lock b/Cargo.lock index 02b12d80..7cd0c7c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1093,7 +1093,7 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "appchain-core-contract-client" version = "0.1.0" -source = "git+https://github.com/byteZorvin/zaun?branch=type-update#fdfc6d4376f680a000fb0bd4b17a85207ad5fefd" +source = "git+https://github.com/byteZorvin/zaun?branch=type-update#36dc1fc5b2affaee69c669aac97a5062622b43b5" dependencies = [ "appchain-utils", "async-trait", @@ -1113,7 +1113,7 @@ dependencies = [ [[package]] name = "appchain-utils" version = "0.1.0" -source = "git+https://github.com/byteZorvin/zaun?branch=type-update#fdfc6d4376f680a000fb0bd4b17a85207ad5fefd" +source = "git+https://github.com/byteZorvin/zaun?branch=type-update#36dc1fc5b2affaee69c669aac97a5062622b43b5" dependencies = [ "async-trait", "color-eyre", diff --git a/crates/settlement-clients/ethereum/src/lib.rs b/crates/settlement-clients/ethereum/src/lib.rs index d8843498..ae3c303d 100644 --- a/crates/settlement-clients/ethereum/src/lib.rs +++ b/crates/settlement-clients/ethereum/src/lib.rs @@ -28,7 +28,7 @@ use mockall::{automock, predicate::*}; use alloy::providers::ProviderBuilder; use conversion::{get_input_data_for_eip_4844, prepare_sidecar}; #[cfg(not(feature = "testing"))] -use settlement_client_interface::{SettlementClient, SettlementConfig, SettlementVerificationStatus}; +use settlement_client_interface::{SettlementClient, SettlementVerificationStatus}; #[cfg(feature = "testing")] use settlement_client_interface::{SettlementClient, SettlementVerificationStatus}; #[cfg(feature = "testing")] @@ -164,11 +164,16 @@ impl SettlementClient for EthereumSettlementClient { &self, program_output: Vec<[u8; 32]>, onchain_data_hash: [u8; 32], - onchain_data_size: usize, + onchain_data_size: [u128; 2], ) -> Result { let program_output: Vec = vec_u8_32_to_vec_u256(program_output.as_slice())?; let onchain_data_hash: U256 = slice_u8_to_u256(&onchain_data_hash)?; - let onchain_data_size: U256 = onchain_data_size.try_into()?; + let mut bytes = [0u8; 32]; + + bytes[0..16].copy_from_slice(&onchain_data_size[0].to_le_bytes()); + bytes[16..32].copy_from_slice(&onchain_data_size[1].to_le_bytes()); + + let onchain_data_size = U256::from_le_bytes(bytes); let tx_receipt = self.core_contract_client.update_state(program_output, onchain_data_hash, onchain_data_size).await?; Ok(format!("0x{:x}", tx_receipt.transaction_hash)) diff --git a/crates/settlement-clients/settlement-client-interface/src/lib.rs b/crates/settlement-clients/settlement-client-interface/src/lib.rs index 88ae00d0..d239c4fb 100644 --- a/crates/settlement-clients/settlement-client-interface/src/lib.rs +++ b/crates/settlement-clients/settlement-client-interface/src/lib.rs @@ -25,7 +25,7 @@ pub trait SettlementClient: Send + Sync { &self, program_output: Vec<[u8; 32]>, onchain_data_hash: [u8; 32], - onchain_data_size: usize, + onchain_data_size: [u128; 2], ) -> Result; /// Should be used to update state on contract and publish the blob on ethereum. diff --git a/crates/settlement-clients/starknet/src/lib.rs b/crates/settlement-clients/starknet/src/lib.rs index 95cd5485..7d0a3fb0 100644 --- a/crates/settlement-clients/starknet/src/lib.rs +++ b/crates/settlement-clients/starknet/src/lib.rs @@ -12,7 +12,7 @@ use color_eyre::Result; use lazy_static::lazy_static; use mockall::{automock, predicate::*}; use starknet::accounts::ConnectedAccount; -use starknet::core::types::TransactionExecutionStatus; +use starknet::core::types::{TransactionExecutionStatus, U256}; use starknet::providers::Provider; use starknet::{ accounts::{ExecutionEncoding, SingleOwnerAccount}, @@ -117,15 +117,15 @@ impl SettlementClient for StarknetSettlementClient { &self, program_output: Vec<[u8; 32]>, onchain_data_hash: [u8; 32], - onchain_data_size: usize, + onchain_data_size: [u128; 2], ) -> Result { let program_output = slice_slice_u8_to_vec_field(program_output.as_slice()); let onchain_data_hash = slice_u8_to_field(&onchain_data_hash); - let onchain_data_size = Felt::from(onchain_data_size); let core_contract: &CoreContract = self.starknet_core_contract_client.as_ref(); + let onchain_data_size = U256::from_words(onchain_data_size[0], onchain_data_size[1]); let invoke_result = core_contract.update_state(program_output, onchain_data_hash, onchain_data_size).await?; - Ok(format!("0x{:x}", invoke_result.transaction_hash)) + Ok(invoke_result.transaction_hash.to_hex_string()) } /// Should verify the inclusion of a tx in the settlement layer diff --git a/crates/settlement-clients/starknet/src/tests/mock_contracts/src/lib.cairo b/crates/settlement-clients/starknet/src/tests/mock_contracts/src/lib.cairo index 7f1d1bf2..43577b0a 100644 --- a/crates/settlement-clients/starknet/src/tests/mock_contracts/src/lib.cairo +++ b/crates/settlement-clients/starknet/src/tests/mock_contracts/src/lib.cairo @@ -6,13 +6,16 @@ pub trait IPiltover { onchain_data_hash: felt252, onchain_data_size: u256 ); + + fn get_is_updated(self: @TContractState, onchain_data_hash: felt252) -> bool; } #[starknet::contract] mod Piltover { + use starknet::storage::Map; #[storage] struct Storage { - balance: felt252, + is_updated: Map, } #[abi(embed_v0)] @@ -22,6 +25,12 @@ mod Piltover { program_output: Span, onchain_data_hash: felt252, onchain_data_size: u256 - ) {} + ) { + self.is_updated.write(onchain_data_hash, true); + } + + fn get_is_updated(self: @ContractState, onchain_data_hash: felt252) -> bool { + self.is_updated.read(onchain_data_hash) + } } } diff --git a/crates/settlement-clients/starknet/src/tests/mod.rs b/crates/settlement-clients/starknet/src/tests/mod.rs index 35e469a2..5c2d0532 100644 --- a/crates/settlement-clients/starknet/src/tests/mod.rs +++ b/crates/settlement-clients/starknet/src/tests/mod.rs @@ -9,7 +9,7 @@ use std::{ collections::HashMap, future::Future, path::{Path, PathBuf}, - process::{Child, Command, Output, Stdio}, + process::{Child, Command, Output}, str::FromStr, time::Duration, }; @@ -183,7 +183,6 @@ impl MadaraCmdBuilder { "--rpc-port".into(), format!("{}", self.port.0), ])) - .stdout(Stdio::piped()) .spawn() .unwrap(); diff --git a/crates/settlement-clients/starknet/src/tests/test.rs b/crates/settlement-clients/starknet/src/tests/test.rs index 669974d8..99c522ae 100644 --- a/crates/settlement-clients/starknet/src/tests/test.rs +++ b/crates/settlement-clients/starknet/src/tests/test.rs @@ -3,19 +3,21 @@ use crate::StarknetSettlementClient; use rstest::{fixture, rstest}; use settlement_client_interface::SettlementClient; use starknet::{ - accounts::{Account, ExecutionEncoding, SingleOwnerAccount}, + accounts::{Account, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount}, contract::ContractFactory, core::types::{ contract::{CompiledClass, SierraClass}, - BlockId, BlockTag, DeclareTransactionResult, Felt, + BlockId, BlockTag, DeclareTransactionResult, Felt, FunctionCall, InvokeTransactionResult, StarknetError, + TransactionExecutionStatus, TransactionStatus, }, - macros::felt, - providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Url}, + macros::{felt, selector}, + providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, ProviderError, Url}, signers::{LocalWallet, SigningKey}, }; use std::env; use std::path::PathBuf; use std::sync::Arc; +use std::time::Duration; use utils::settings::env::EnvSettingsProvider; use utils::settings::Settings; @@ -35,7 +37,6 @@ pub async fn spin_up_madara() -> MadaraCmd { env::set_current_dir(&project_root).expect("Failed to set working directory"); - let _ = env_logger::builder().is_test(true).try_init(); let mut node = MadaraCmdBuilder::new() .args([ "--network", @@ -47,25 +48,69 @@ pub async fn spin_up_madara() -> MadaraCmd { "--devnet", "--preset=test", "--no-l1-sync", + "--rpc-cors", + "all", ]) .run(); + println!("check here"); node.wait_for_ready().await; + println!("check here 2"); node } -// #[fixture] -// async fn setup() -> (SingleOwnerAccount, LocalWallet>, MadaraCmd) { +#[allow(unused)] +async fn wait_for_tx_success( + account: &SingleOwnerAccount, LocalWallet>, + transaction_hash: Felt, + duration: Duration, +) -> bool { + let mut attempt = 0; + loop { + attempt += 1; + let reciept = match account.provider().get_transaction_status(transaction_hash).await { + Ok(reciept) => reciept, + Err(ProviderError::StarknetError(StarknetError::TransactionHashNotFound)) => { + tokio::time::sleep(duration).await; + continue; + } + _ => panic!("Unknown error"), + }; + + match reciept { + TransactionStatus::Received => (), + TransactionStatus::Rejected => return false, + TransactionStatus::AcceptedOnL2(status) => match status { + TransactionExecutionStatus::Succeeded => return true, + TransactionExecutionStatus::Reverted => return false, + }, + TransactionStatus::AcceptedOnL1(status) => match status { + TransactionExecutionStatus::Succeeded => return true, + TransactionExecutionStatus::Reverted => return false, + }, + } + + if attempt >= 5 { + return false; + } + + // This is done, since currently madara does not increment nonce for pending transactions + tokio::time::sleep(duration).await; + } +} + #[fixture] -async fn setup() -> SingleOwnerAccount, LocalWallet> { - // let madara_process = spin_up_madara().await; - // println!("RPC url {:?}", madara_process.rpc_url); - dotenvy::from_filename_override(".env").expect("Failed to load the .env file"); +async fn setup() -> (SingleOwnerAccount, LocalWallet>, MadaraCmd) { + let _ = env_logger::builder().is_test(true).try_init(); + dotenvy::from_filename_override(".env.test").expect("Failed to load the .env file"); + + let madara_process = spin_up_madara().await; + env::set_var("STARKNET_RPC_URL", madara_process.rpc_url.to_string()); + println!("RPC url {:?}", madara_process.rpc_url); let env_settings = EnvSettingsProvider::default(); let rpc_url = Url::parse(&env_settings.get_settings_or_panic("STARKNET_RPC_URL")).unwrap(); println!("RPC url {:?}", rpc_url); // let endpoint = madara_process.rpc_url.join("/health").unwrap(); - // let endpoint = rpc_url.join("/health").unwrap(); // let response = reqwest::get(endpoint.clone()).await.expect("Failed to connect to Provider"); // assert!(response.status().is_success(), "Failed to connect to Provider"); @@ -82,19 +127,18 @@ async fn setup() -> SingleOwnerAccount, LocalWallet // `SingleOwnerAccount` defaults to checking nonce and estimating fees against the latest // block. Optionally change the target block to pending with the following line: account.set_block_id(BlockId::Tag(BlockTag::Pending)); - // (account, madara_process) - account + (account, madara_process) } #[rstest] #[tokio::test] -// async fn test_deployment(#[future] setup: (SingleOwnerAccount, LocalWallet>, MadaraCmd)) { -async fn test_deployment(#[future] setup: SingleOwnerAccount, LocalWallet>) { - // let (account, madara_process) = setup.await; - let account = setup.await; +async fn test_deployment(#[future] setup: (SingleOwnerAccount, LocalWallet>, MadaraCmd)) { + // async fn test_deployment(#[future] setup: SingleOwnerAccount, LocalWallet>) { + let (account, _) = setup.await; + // let account = setup.await; // println!("the db being used is {:?}", madara_process.tempdir); - let account = Arc::new(account); + // let account = Arc::new(account); // NOTE: you will need to declare this class first let sierra_class: SierraClass = serde_json::from_reader( @@ -121,50 +165,71 @@ async fn test_deployment(#[future] setup: SingleOwnerAccount, LocalWallet>) { - dotenvy::from_filename(".env.test").expect("Failed to load the .env file"); - // let (account, madara_process) = setup.await; - let account = setup.await; - - // println!("the db being used is {:?}", madara_process.tempdir); +async fn test_settle(#[future] setup: (SingleOwnerAccount, LocalWallet>, MadaraCmd)) { + let (account, _madara_process) = setup.await; let account = Arc::new(account); // NOTE: you will need to declare this class first let sierra_class: SierraClass = serde_json::from_reader( std::fs::File::open("/Users/bytezorvin/work/karnot/orchestrator/crates/settlement-clients/starknet/src/tests/mock_contracts/target/dev/mock_contracts_Piltover.contract_class.json").unwrap(), ) - .unwrap(); + .expect("Failed to parse SierraClass"); let compiled_class: CompiledClass = serde_json::from_reader( std::fs::File::open("/Users/bytezorvin/work/karnot/orchestrator/crates/settlement-clients/starknet/src/tests/mock_contracts/target/dev/mock_contracts_Piltover.compiled_contract_class.json").unwrap(), ) - .unwrap(); + .expect("Failed to parse CompiledClass"); + + let flattened_class = sierra_class.clone().flatten().unwrap(); + let compiled_class_hash = compiled_class.class_hash().unwrap(); - // let flattened_class = sierra_class.clone().flatten().unwrap(); - // let compiled_class_hash = compiled_class.class_hash().unwrap(); - // let DeclareTransactionResult { transaction_hash: _, class_hash } = - // account.declare_v2(Arc::new(flattened_class.clone()), compiled_class_hash).send().await.unwrap(); - // assert!(flattened_class.class_hash() == class_hash, "Class hash declared is not same"); + let DeclareTransactionResult { transaction_hash: declare_tx_hash, class_hash: _ } = + account.declare_v2(Arc::new(flattened_class.clone()), compiled_class_hash).send().await.unwrap(); + println!("declare tx hash {:?}", declare_tx_hash); - // This is done, since currently madara does not increment nonce for pending transactions - // tokio::time::sleep(tokio::time::Duration::from_secs(4)).await; + let is_success = wait_for_tx_success(&account, declare_tx_hash, Duration::from_secs(2)).await; + assert!(is_success, "Declare trasactiion failed"); - // let contract_factory = ContractFactory::new(flattened_class.class_hash(), account); - // let deploy_v1 = contract_factory.deploy_v1(vec![], felt!("1122"), false); - // let deployed_address = deploy_v1.deployed_address(); + let contract_factory = ContractFactory::new(flattened_class.class_hash(), account.clone()); + let deploy_v1 = contract_factory.deploy_v1(vec![], felt!("1122"), false); + let deployed_address = deploy_v1.deployed_address(); - let deployed_address = "0x067b25d85c42bae8f3fe833ab5ff97368e1c39019d34f02906e1cc6280f80e50"; - env::set_var("STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", deployed_address); - // env::set_var("STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", deployed_address.to_hex_string()); - // deploy_v1.send().await.expect("Unable to deploy contract"); + env::set_var("STARKNET_CAIRO_CORE_CONTRACT_ADDRESS", deployed_address.to_hex_string()); + let InvokeTransactionResult { transaction_hash: deploy_tx_hash } = + deploy_v1.send().await.expect("Unable to deploy contract"); - // This is done, since currently madara does not increment nonce for pending transactions - tokio::time::sleep(tokio::time::Duration::from_secs(4)).await; + let is_success = wait_for_tx_success(&account, deploy_tx_hash, Duration::from_secs(2)).await; + assert!(is_success, "Deploy trasaction failed"); let env_settings = EnvSettingsProvider {}; let settlement_client = StarknetSettlementClient::new_with_settings(&env_settings).await; let onchain_data_hash = [1; 32]; let mut program_output = Vec::with_capacity(32); program_output.fill(onchain_data_hash); - settlement_client.update_state_calldata(program_output, onchain_data_hash, 1).await.unwrap(); + let update_state_tx_hash = settlement_client + .update_state_calldata(program_output, onchain_data_hash, [1, 1]) + .await + .expect("Sending Update state"); + + let is_success = wait_for_tx_success( + &account, + Felt::from_hex(&update_state_tx_hash).expect("Incorrect transaction hash"), + Duration::from_secs(2), + ) + .await; + assert!(is_success, "Update state transaction failed/reverted"); + + let call_result = account + .provider() + .call( + FunctionCall { + contract_address: deployed_address, + entry_point_selector: selector!("get_is_updated"), + calldata: vec![Felt::from_bytes_be_slice(&onchain_data_hash)], + }, + BlockId::Tag(BlockTag::Latest), + ) + .await + .expect("failed to call the contract"); + assert!(call_result[0] == true.into(), "Should be updated"); }