diff --git a/README.md b/README.md index a96942a..d127a16 100644 --- a/README.md +++ b/README.md @@ -79,50 +79,4 @@ async fn work_existing_block(clients: HashMap ⚠️ Structure members must have the **exact same name** as the json fields expected as an RPC call result. - -- For fields which are themselves a JSON object, use another struct to represent this sub-object. -- For fields that might be optional, use `Option`. - -Try and test as many edge cases as possible. Ths mostly includes optional parameters. You can find a list -of Starknet RPC call and their arguments / return data [here](https://playground.open-rpc.org/?uiSchema%5BappBar%5D%5Bui:splitView%5D=false&schemaUrl=https://raw.githubusercontent.com/starkware-libs/starknet-specs/master/api/starknet_api_openrpc.json&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:darkMode%5D=true&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false) -======= -``` ->>>>>>> Stashed changes +} \ No newline at end of file diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 2c1f661..e8ed70f 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -1,4 +1,4 @@ -use macro_utils::{extract_u64_from_expr, get_block_number}; +use macro_utils::{extract_expr_to_str, extract_expr_to_u64, get_rpc_data, RpcData}; use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{ @@ -24,19 +24,21 @@ pub fn logging(_: TokenStream, input: TokenStream) -> TokenStream { input.into_token_stream().into() } -struct ArgsRequire { +struct MacroDataRequire { pub block_min: u64, pub block_max: u64, + pub spec_version: Option, pub err: Result<(), Path>, } -impl Parse for ArgsRequire { +impl Parse for MacroDataRequire { fn parse(input: ParseStream) -> syn::Result { let args = input.parse_terminated(MetaNameValue::parse, Token![,])?; let mut parsed_params = Self { block_min: 0, - block_max: 0, + block_max: u64::MAX, + spec_version: None, err: Ok(()), }; @@ -44,11 +46,17 @@ impl Parse for ArgsRequire { match arg.path.get_ident() { Some(ident) => match ident.to_string().as_str() { "block_min" => { - parsed_params.block_min = extract_u64_from_expr(arg.value).unwrap_or(0); + parsed_params.block_min = extract_expr_to_u64(arg.value).unwrap_or(0); } "block_max" => { parsed_params.block_max = - extract_u64_from_expr(arg.value).unwrap_or(u64::MAX); + extract_expr_to_u64(arg.value).unwrap_or(u64::MAX); + } + "spec_version" => { + parsed_params.spec_version = match extract_expr_to_str(arg.value) { + Ok(s) => Some(s), + Err(_) => None, + } } _ => { parsed_params.err = Err(arg.path); @@ -62,12 +70,27 @@ impl Parse for ArgsRequire { } } +impl MacroDataRequire { + fn should_ignore(self, data: RpcData) -> bool { + let Self { + block_min, + block_max, + spec_version, + err: _, + } = self; + + (data.block_number >= block_min) + && (data.block_number <= block_max) + && (data.spec_version == spec_version.unwrap_or(String::from(""))) + } +} + #[proc_macro_attribute] pub fn require(args: TokenStream, item: TokenStream) -> TokenStream { - let bn = get_block_number(); - let args_parsed = parse_macro_input!(args as ArgsRequire); + let block_data = get_rpc_data(); + let macro_data = parse_macro_input!(args as MacroDataRequire); - if bn >= args_parsed.block_min && bn <= args_parsed.block_max { + if macro_data.should_ignore(block_data) { item } else { let mut func = parse_macro_input!(item as ItemFn); diff --git a/macro_utils/src/lib.rs b/macro_utils/src/lib.rs index da75061..6011160 100644 --- a/macro_utils/src/lib.rs +++ b/macro_utils/src/lib.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use serde::Deserialize; use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}; use std::{fs::File, io::Read}; @@ -25,7 +26,12 @@ impl TestConfig { } } -pub fn get_block_number() -> u64 { +pub struct RpcData { + pub block_number: u64, + pub spec_version: String, +} + +pub fn get_rpc_data() -> RpcData { let config = TestConfig::new("./secret.json").expect("'./secret.json' must contain correct node urls"); let deoxys = JsonRpcClient::new(HttpTransport::new( @@ -34,17 +40,33 @@ pub fn get_block_number() -> u64 { let rt = runtime::Runtime::new().unwrap(); - rt.block_on(async { deoxys.block_number().await.unwrap() }) + rt.block_on(async { + RpcData { + block_number: deoxys.block_number().await.unwrap(), + spec_version: deoxys.spec_version().await.unwrap(), + } + }) +} + +pub fn extract_expr_to_str(expr: Expr) -> anyhow::Result { + match expr { + Expr::Lit(expr_lit) => match expr_lit.lit { + Lit::Str(lit_str) => anyhow::Ok(lit_str.value()), + _ => Err(anyhow!("Not a string literal")), + }, + _ => Err(anyhow!("Not a literal expression")), + } } -pub fn extract_u64_from_expr(expr: Expr) -> Result { +pub fn extract_expr_to_u64(expr: Expr) -> anyhow::Result { match expr { Expr::Lit(expr_lit) => match expr_lit.lit { - Lit::Int(lit_int) => lit_int - .base10_parse::() - .map_err(|_| "Failed to parse integer".to_string()), - _ => Err("Not an integer literal".to_string()), + Lit::Int(lit_int) => match lit_int.base10_parse::() { + Ok(n) => anyhow::Ok(n), + Err(_) => Err(anyhow!("Failed to convert literal")), + }, + _ => Err(anyhow!("Not an integer literal")), }, - _ => Err("Not a literal expression".to_string()), + _ => Err(anyhow!("Not a literal expression")), } } diff --git a/unit_tests/src/constants.rs b/unit_tests/src/constants.rs index 070ee5b..62345b4 100644 --- a/unit_tests/src/constants.rs +++ b/unit_tests/src/constants.rs @@ -151,4 +151,5 @@ pub const ARGENT_CONTRACT_ADDRESS: &str = ""; pub const ERR_DEOXYS: &str = "Error waiting for response from Deoxys client"; pub const ERR_PATHFINDER: &str = "Error waiting for response from Pathfinder client"; -pub const RPC_SPEC: &str = "0.5.1"; +pub const SPEC_0_5_1: &str = "0.5.1"; +pub const SPEC_0_6_0: &str = "0.6.0"; diff --git a/unit_tests/tests/test_block_hash_and_number.rs b/unit_tests/tests/test_block_hash_and_number.rs index 90153f1..3cbcc0f 100644 --- a/unit_tests/tests/test_block_hash_and_number.rs +++ b/unit_tests/tests/test_block_hash_and_number.rs @@ -1,5 +1,6 @@ mod common; use common::*; +use r#macro::require; use std::collections::HashMap; @@ -14,6 +15,7 @@ use starknet_providers::{ /// purpose: get block hash and number on latest block. /// success case: retrieves correct block hash and number. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_latest_block(clients: HashMap>) { diff --git a/unit_tests/tests/test_block_number.rs b/unit_tests/tests/test_block_number.rs index c7721d7..f545ffb 100644 --- a/unit_tests/tests/test_block_number.rs +++ b/unit_tests/tests/test_block_number.rs @@ -14,6 +14,7 @@ use starknet_providers::{ /// purpose: call blockNumber on latest block. /// success case: must return valid non-zero block number. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_existing_block(clients: HashMap>) { diff --git a/unit_tests/tests/test_call.rs b/unit_tests/tests/test_call.rs index cd70692..d0059f7 100644 --- a/unit_tests/tests/test_call.rs +++ b/unit_tests/tests/test_call.rs @@ -17,6 +17,7 @@ use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Provid /// purpose: function request `name` to StarkGate ETH bridge contract /// fail case: invalid block /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_block(clients: HashMap>) { @@ -46,6 +47,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -77,6 +79,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -141,6 +145,7 @@ async fn fail_missing_contract_call_data(clients: HashMap>) { @@ -170,6 +175,7 @@ async fn fail_invalid_contract_call_data(clients: HashMap>) { @@ -199,6 +205,7 @@ async fn fail_too_many_call_data(clients: HashMap>) { @@ -241,6 +248,7 @@ async fn work_correct_call(clients: HashMap /// purpose: function request `balanceOf` to StarkGate ETH bridge contract /// success case: must return non-zero balance /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_correct_call_with_args(clients: HashMap>) { @@ -283,6 +291,7 @@ async fn work_correct_call_with_args(clients: HashMap>) { diff --git a/unit_tests/tests/test_chain_id.rs b/unit_tests/tests/test_chain_id.rs index f00230f..4cb5bca 100644 --- a/unit_tests/tests/test_chain_id.rs +++ b/unit_tests/tests/test_chain_id.rs @@ -10,6 +10,7 @@ use std::collections::HashMap; /// purpose: get currently configured Starknet chain id /// success case: retrieve correct chain id /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn chain_id(clients: HashMap>) { diff --git a/unit_tests/tests/test_estimate_message_fee.rs b/unit_tests/tests/test_estimate_message_fee.rs index 5b2cf70..e69d5df 100644 --- a/unit_tests/tests/test_estimate_message_fee.rs +++ b/unit_tests/tests/test_estimate_message_fee.rs @@ -40,6 +40,7 @@ pub fn get_message_from_l1( } } +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[ignore = "Need to fix unwrap on error due to empty constants"] @@ -64,6 +65,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -29,6 +30,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -50,6 +52,7 @@ async fn work_with_latest_block(clients: HashMap>) { @@ -71,6 +74,7 @@ async fn work_with_block_one_num(clients: HashMap>) { @@ -97,6 +101,7 @@ async fn work_with_block_one_hash(clients: HashMap>) { @@ -41,6 +42,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -71,6 +73,7 @@ async fn work_existing_block(clients: HashMap>) { @@ -29,6 +30,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -50,6 +52,7 @@ async fn work_with_latest_block(clients: HashMap>) { @@ -71,6 +74,7 @@ async fn work_with_block_one_num(clients: HashMap>) { @@ -97,6 +101,7 @@ async fn work_with_block_one_hash(clients: HashMap>) { @@ -41,6 +42,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -65,6 +67,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -39,6 +40,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -66,6 +68,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -96,6 +99,7 @@ async fn work_block_latest(clients: HashMap /// purpose: call getClassHashAt on pending block. /// success case: retrieve valid class hash. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_block_pending(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_events.rs b/unit_tests/tests/test_get_events.rs index 9c41902..a21e627 100644 --- a/unit_tests/tests/test_get_events.rs +++ b/unit_tests/tests/test_get_events.rs @@ -31,6 +31,7 @@ use tokio::task::JoinSet; /// purpose: call getEvents on an invalid block number. /// fail case: invalid block number (invalid param). /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -57,6 +58,7 @@ async fn fail_invalid_block_number(deoxys: JsonRpcClient) { /// purpose: call getEvents on an invalid event selector. /// fail case: invalid event selector. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -82,6 +84,7 @@ async fn fail_invalid_keys(deoxys: JsonRpcClient) { /// purpose: call getEvents on an invalid event selector. /// fail case: invalid event selector. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -109,6 +112,7 @@ async fn fail_invalid_block_range(deoxys: JsonRpcClient) { /// purpose: call getEvents on a valid block with a no selector. /// success case: retrieves the first 100 events of that block. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -144,6 +148,7 @@ async fn work_valid_call_no_selector( /// purpose: call getEvents on a valid block with a single selector. /// success case: valid events format, events point to valid transactions. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -180,6 +185,7 @@ async fn work_valid_call_single_selector( /// success case: retrieves all events matching the selector in the first 100 events of that block /// + valid event format and valid transactions. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -213,7 +219,7 @@ async fn work_valid_call_multiple_selector( async fn get_events( client: &JsonRpcClient, - keys: &Vec>, + keys: &[Vec], block_nu: u64, block_range: u64, ) -> Result { @@ -228,7 +234,7 @@ async fn get_events( address: None, // and keys used to filter out events. Keys can include a hash of the event // and even event return values for further filtering - keys: Some(keys.clone()), + keys: Some(keys.to_vec()), }, // in cases were a first search does not yield enough results, a continuation key // can be used to keep searching from the point of the last getEvent search diff --git a/unit_tests/tests/test_get_nonce.rs b/unit_tests/tests/test_get_nonce.rs index 9c30955..8261d38 100644 --- a/unit_tests/tests/test_get_nonce.rs +++ b/unit_tests/tests/test_get_nonce.rs @@ -29,6 +29,7 @@ use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Provid /// purpose: call getNonce on invalid block. /// fail case: invalid block. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_block(clients: HashMap>) { @@ -54,6 +55,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -84,7 +86,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -107,7 +109,7 @@ async fn work_erc721_contract(clients: HashMap>) { @@ -130,7 +132,7 @@ async fn work_erc20_contract(clients: HashMap>) { @@ -163,7 +165,7 @@ async fn work_account_contract(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_state_update.rs b/unit_tests/tests/test_get_state_update.rs index 06cb190..2d939bc 100644 --- a/unit_tests/tests/test_get_state_update.rs +++ b/unit_tests/tests/test_get_state_update.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; // # Errors // * `block_not_found` - If the block is not found or invalid +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[ignore = "Need to fix unwrap on error due to empty constants"] @@ -32,6 +33,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -41,6 +42,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -69,6 +71,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -92,6 +95,7 @@ async fn fail_invalid_storage_key(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs b/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs index 351e20b..b31493d 100644 --- a/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs +++ b/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs @@ -13,6 +13,7 @@ use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Provid /// purpose: call on non-existent block. /// fail case: invalid block /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existent_block(clients: HashMap>) { @@ -35,6 +36,7 @@ async fn fail_non_existent_block(clients: HashMap>) { @@ -59,6 +61,7 @@ async fn fail_non_existent_block_index(clients: HashMap>) { @@ -98,6 +101,7 @@ async fn work_deploy_invoke(clients: HashMap>) { @@ -137,6 +141,7 @@ async fn work_deploy_l1_handler(clients: HashMap>) { @@ -176,6 +181,7 @@ async fn work_deploy_declare(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_by_hash.rs b/unit_tests/tests/test_get_transaction_by_hash.rs index f526c27..47791a9 100644 --- a/unit_tests/tests/test_get_transaction_by_hash.rs +++ b/unit_tests/tests/test_get_transaction_by_hash.rs @@ -13,6 +13,7 @@ use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Provid /// purpose: call getTransactionHash on non existent transaction. /// fail case: transaction does not exist. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_transaction(clients: HashMap>) { @@ -37,6 +38,7 @@ async fn fail_non_existing_transaction(clients: HashMap>) { @@ -63,6 +65,7 @@ async fn work_transaction_invoke(clients: HashMap>) { @@ -89,6 +92,7 @@ async fn work_transaction_l1_handler(clients: HashMap>) { @@ -117,6 +121,7 @@ async fn work_transaction_declare(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_receipt.rs b/unit_tests/tests/test_get_transaction_receipt.rs index ec9e59e..c93ff24 100644 --- a/unit_tests/tests/test_get_transaction_receipt.rs +++ b/unit_tests/tests/test_get_transaction_receipt.rs @@ -12,6 +12,7 @@ use starknet_providers::{ }; // invalid transaction_hash +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_invalid_transaction_hash(clients: HashMap>) { @@ -31,6 +32,7 @@ async fn fail_invalid_transaction_hash(clients: HashMap>) { @@ -56,6 +58,7 @@ async fn work_with_rejected_transaction(clients: HashMap>) { @@ -81,6 +84,7 @@ async fn work_with_first_transaction(clients: HashMap>) { @@ -106,6 +110,7 @@ async fn work_with_deploy(clients: HashMap> } ///invoke transaction +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_with_invoke(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_status.rs b/unit_tests/tests/test_get_transaction_status.rs index b9ee3a3..11f1671 100644 --- a/unit_tests/tests/test_get_transaction_status.rs +++ b/unit_tests/tests/test_get_transaction_status.rs @@ -15,6 +15,7 @@ use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider, Provid /// purpose: call getTransactionStatus on non-existent transaction hash. /// fail case: non-existent transaction hash. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_invalid_transaction(clients: HashMap>) { @@ -39,6 +40,7 @@ async fn fail_invalid_transaction(clients: HashMap>) { @@ -65,6 +67,7 @@ async fn work_transaction_accepted_on_l1(clients: HashMap>) { @@ -106,6 +109,7 @@ async fn work_transaction_accepted_on_l2(clients: HashMap>) { diff --git a/unit_tests/tests/test_syncing.rs b/unit_tests/tests/test_syncing.rs index b03e8b6..2be00d8 100644 --- a/unit_tests/tests/test_syncing.rs +++ b/unit_tests/tests/test_syncing.rs @@ -11,6 +11,7 @@ use std::collections::HashMap; /// purpose: returns starknet sync status /// success case: sync status matches between providers (NOT DETERMINISTIC) /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn syncing(clients: HashMap>) {