Skip to content

Commit

Permalink
feat : added tests for starknet client
Browse files Browse the repository at this point in the history
  • Loading branch information
ocdbytes committed Dec 20, 2024
1 parent abc808e commit a49e209
Show file tree
Hide file tree
Showing 10 changed files with 523 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/rust-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
while ! nc -z localhost 8545; do
sleep 1
done
- name: Download madara binary for l2 client testing
run: |
curl -L https://madara-test-binary.s3.us-west-1.amazonaws.com/madara -o ./test-artifacts/madara
chmod +x ./test-artifacts/madara
- name: Run unit tests
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ tmp/
# Running madara with make and docker compose
.secrets
image.tar.gz

# madara test artifacts for testing l2 clients for appchains
test-artifacts/madara
4 changes: 4 additions & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions crates/client/settlement_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ starknet_api.workspace = true


# Other
assert_matches = "1.5.0"
alloy.workspace = true
anyhow.workspace = true
bigdecimal.workspace = true
Expand All @@ -44,6 +45,9 @@ serde = { workspace = true, default-features = true }
serde_json = "1"
starknet-providers = { workspace = true }
starknet-core = { workspace = true }
starknet-accounts = "0.11.0"
starknet-signers = { workspace = true }
starknet-contract = "0.11.0"
thiserror.workspace = true
time = "0.3.36"
tokio = { workspace = true, features = [
Expand Down
218 changes: 209 additions & 9 deletions crates/client/settlement_client/src/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ use tokio::time::sleep;
use tracing::{error, trace};
use url::Url;

#[cfg(test)]
mod utils;

#[derive(Debug)]
pub struct StarknetClient {
pub provider: Arc<JsonRpcClient<HttpTransport>>,
pub l2_core_contract: Felt,
Expand Down Expand Up @@ -66,6 +70,9 @@ impl ClientTrait for StarknetClient {
Self: Sized,
{
let provider = JsonRpcClient::new(HttpTransport::new(config.url));
// Check if l2 contract exists :
// If contract is not there this will error out.
provider.get_class_at(BlockId::Tag(BlockTag::Latest), config.l2_contract_address).await?;
Ok(Self {
provider: Arc::new(provider),
l2_core_contract: config.l2_contract_address,
Expand All @@ -80,9 +87,11 @@ impl ClientTrait for StarknetClient {

async fn get_last_event_block_number(&self) -> anyhow::Result<u64> {
let latest_block = self.get_latest_block_number().await?;
// If block on l2 is not greater than or equal to 6000 we will consider the last block to 0.
let last_block = if latest_block <= 6000 { 0 } else { 6000 };
let last_events = self
.get_events(
BlockId::Number(latest_block - 6000),
BlockId::Number(last_block),
BlockId::Number(latest_block),
self.l2_core_contract,
// taken from : https://github.com/keep-starknet-strange/piltover/blob/main/src/appchain.cairo#L102
Expand All @@ -94,7 +103,7 @@ impl ClientTrait for StarknetClient {
match last_update_state_event {
Some(event) => {
/*
Github Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/appchain.cairo#L101
GitHub Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/appchain.cairo#L101
Event description :
------------------
#[derive(Drop, starknet::Event)]
Expand All @@ -105,7 +114,11 @@ impl ClientTrait for StarknetClient {
}
*/
assert_eq!(event.data.len(), 3, "Event response invalid !!");
Ok(event.data[1].to_u64().unwrap())
// Block number management in case of pending block number events.
match event.block_number {
Some(block_number) => Ok(block_number),
None => Ok(self.get_latest_block_number().await? + 1),
}
}
None => {
bail!("No event found")
Expand All @@ -120,13 +133,13 @@ impl ClientTrait for StarknetClient {
FunctionCall {
contract_address: self.l2_core_contract,
/*
Github Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
GitHub Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
Function Call response : (StateRoot, BlockNumber, BlockHash)
*/
entry_point_selector: get_selector_from_name("get_state")?,
calldata: vec![],
},
BlockId::Tag(BlockTag::Latest),
BlockId::Tag(BlockTag::Pending),
)
.await?;
assert_eq!(call_res.len(), 3, "Call response invalid !!");
Expand All @@ -141,13 +154,13 @@ impl ClientTrait for StarknetClient {
FunctionCall {
contract_address: self.l2_core_contract,
/*
Github Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
GitHub Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
Function Call response : (StateRoot, BlockNumber, BlockHash)
*/
entry_point_selector: get_selector_from_name("get_state")?,
calldata: vec![],
},
BlockId::Tag(BlockTag::Latest),
BlockId::Tag(BlockTag::Pending),
)
.await?;
assert_eq!(call_res.len(), 3, "Call response invalid !!");
Expand All @@ -162,13 +175,13 @@ impl ClientTrait for StarknetClient {
FunctionCall {
contract_address: self.l2_core_contract,
/*
Github Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
GitHub Ref : https://github.com/keep-starknet-strange/piltover/blob/main/src/state/component.cairo#L59
Function Call response : (StateRoot, BlockNumber, BlockHash)
*/
entry_point_selector: get_selector_from_name("get_state")?,
calldata: vec![],
},
BlockId::Tag(BlockTag::Latest),
BlockId::Tag(BlockTag::Pending),
)
.await?;
assert_eq!(call_res.len(), 3, "Call response invalid !!");
Expand Down Expand Up @@ -268,3 +281,190 @@ impl StarknetClient {
Ok(event_vec)
}
}

#[cfg(test)]
pub mod starknet_client_tests {
use crate::client::ClientTrait;
use crate::gas_price::L1BlockMetrics;
use crate::starknet::utils::{prepare_starknet_client_test, send_state_update, MADARA_PORT};
use crate::starknet::{StarknetClient, StarknetClientConfig};
use crate::state_update::StateUpdate;
use serial_test::serial;
use starknet_types_core::felt::Felt;
use std::str::FromStr;
use std::time::Duration;
use tokio::time::sleep;
use url::Url;

#[serial]
#[tokio::test]
async fn fail_create_new_client_contract_does_not_exists() -> anyhow::Result<()> {
prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: Felt::from_str("0xdeadbeef")?,
l1_block_metrics,
})
.await;
assert!(starknet_client.is_err(), "Should fail to create a new client");
Ok(())
}

#[serial]
#[tokio::test]
async fn create_new_client_contract_exists_starknet_client() -> anyhow::Result<()> {
// Here we need to have madara variable otherwise it will
// get dropped and will kill the madara.
#[allow(unused_variables)]
let (_, deployed_address, madara) = prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: deployed_address,
l1_block_metrics,
})
.await;
assert!(starknet_client.is_ok(), "Should not fail to create a new client");
Ok(())
}

#[serial]
#[tokio::test]
async fn get_last_event_block_number_works_starknet_client() -> anyhow::Result<()> {
// Here we need to have madara variable otherwise it will
// get dropped and will kill the madara.
#[allow(unused_variables)]
let (account, deployed_address, madara) = prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: deployed_address,
l1_block_metrics,
})
.await?;

// sending state updates :
send_state_update(
&account,
deployed_address,
StateUpdate {
block_number: 99,
global_root: Felt::from_hex("0xdeadbeef")?,
block_hash: Felt::from_hex("0xdeadbeef")?,
},
)
.await?;
let last_event_block_number = send_state_update(
&account,
deployed_address,
StateUpdate {
block_number: 100,
global_root: Felt::from_hex("0xdeadbeef")?,
block_hash: Felt::from_hex("0xdeadbeef")?,
},
)
.await?;

// It takes time on madara for events to be stored
sleep(Duration::from_secs(10)).await;

let latest_event_block_number = starknet_client.get_last_event_block_number().await?;
assert_eq!(latest_event_block_number, last_event_block_number, "Latest event should have block number 100");
Ok(())
}

#[serial]
#[tokio::test]
async fn get_last_verified_block_hash_works_starknet_client() -> anyhow::Result<()> {
// Here we need to have madara variable otherwise it will
// get dropped and will kill the madara.
#[allow(unused_variables)]
let (account, deployed_address, madara) = prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: deployed_address,
l1_block_metrics,
})
.await?;

// sending state updates :
let data_felt = Felt::from_hex("0xdeadbeef")?;
send_state_update(
&account,
deployed_address,
StateUpdate { block_number: 100, global_root: data_felt, block_hash: data_felt },
)
.await?;
sleep(Duration::from_secs(5)).await;

let last_verified_block_hash = starknet_client.get_last_verified_block_hash().await?;
assert_eq!(last_verified_block_hash, data_felt, "Block hash should match");

Ok(())
}

#[serial]
#[tokio::test]
async fn get_last_state_root_works_starknet_client() -> anyhow::Result<()> {
// Here we need to have madara variable otherwise it will
// get dropped and will kill the madara.
#[allow(unused_variables)]
let (account, deployed_address, madara) = prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: deployed_address,
l1_block_metrics,
})
.await?;

// sending state updates :
let data_felt = Felt::from_hex("0xdeadbeef")?;
send_state_update(
&account,
deployed_address,
StateUpdate { block_number: 100, global_root: data_felt, block_hash: data_felt },
)
.await?;
sleep(Duration::from_secs(5)).await;

let last_verified_state_root = starknet_client.get_last_state_root().await?;
assert_eq!(last_verified_state_root, data_felt, "Last state root should match");

Ok(())
}

#[serial]
#[tokio::test]
async fn get_last_verified_block_number_works_starknet_client() -> anyhow::Result<()> {
// Here we need to have madara variable otherwise it will
// get dropped and will kill the madara.
#[allow(unused_variables)]
let (account, deployed_address, madara) = prepare_starknet_client_test().await?;
let l1_block_metrics = L1BlockMetrics::register()?;
let starknet_client = StarknetClient::new(StarknetClientConfig {
url: Url::parse(format!("http://127.0.0.1:{}", MADARA_PORT).as_str())?,
l2_contract_address: deployed_address,
l1_block_metrics,
})
.await?;

// sending state updates :
let data_felt = Felt::from_hex("0xdeadbeef")?;
let block_number = 100;
send_state_update(
&account,
deployed_address,
StateUpdate { block_number, global_root: data_felt, block_hash: data_felt },
)
.await?;
sleep(Duration::from_secs(5)).await;

let last_verified_block_number = starknet_client.get_last_verified_block_number().await?;
assert_eq!(last_verified_block_number, block_number, "Last verified block should match");

Ok(())
}
}
Loading

0 comments on commit a49e209

Please sign in to comment.