Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Adding add_invoke_transaction and deploy_account_transaction RPC test #49

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions unit_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ starknet-accounts = { git = "https://github.com/xJonathanLEI/starknet-rs.git", r
starknet-signers = { git = "https://github.com/xJonathanLEI/starknet-rs.git", rev = "96c6803", default-features = false }
env_logger = "0.10.1"
macro_utils = { path = "../macro_utils/" }
rand = "0.8.5"
serde_json = "1.0"

[dev-dependencies]
jsonrpsee = { version = "0.21.0", features = ["client"] }
Expand Down
148 changes: 148 additions & 0 deletions unit_tests/tests/test_add_invoke_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#![feature(assert_matches)]

mod common;
use common::*;
use starknet_core::types::{
BroadcastedInvokeTransaction, FieldElement, StarknetError, TransactionStatus,
};
use starknet_providers::{
jsonrpc::{HttpTransport, JsonRpcClient},
Provider, ProviderError,
};
use std::assert_matches::assert_matches;
use std::thread;
use std::time::Duration;

/// Test for the `add_invoke_transaction` Deoxys RPC method
/// Submit a new transaction to be added to the chain
///
/// # Arguments
/// * `invoke_transaction` - An invoke transaction,
/// with following fields:
/// * `type` - INVOKE
/// * `sender_address` - The address of the sender
/// * `calldata` - The calldata to send
/// * `max_fee` - The maximum fees sender is willing to pay
/// * `version` - The version of the transaction
/// * `signature` - The transaction signature
/// * `nonce` - The nonce of the transaction
///
/// # Returns
/// * `result` - The result of the transaction submission, with the transaction hash that has been submitted
///
/// # Errors
/// * `invalid_transaction_nonce` - If the transaction nonce is invalid
/// * `insufficient_account_balance` - If the account balance is insufficient
/// * `insufficient_max_fee` - If the max fee is insufficient
/// * `invalid_transaction_nonce` - If the transaction nonce is invalid
/// * `validation_failure` - If the transaction validation fails
/// * `non_account` - If the sender address is not a valid account
/// * `duplicate_transaction` - If a transaction with same params already exists
/// * `unsupported_transaction_version` - If the transaction version is not supported
/// * `unexpected_error` - If an unexpected error occurs

/// Following tests runs using V1 Invoke Transaction (params follow starknet-rs implementation)
#[rstest]
#[tokio::test]
async fn fail_if_param_(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_invoke_transaction = BroadcastedInvokeTransaction {
sender_address: FieldElement::from_hex_be("valid_address").unwrap(),
calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()],
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid
is_query: false,
};

let response_deoxys = deoxys
.add_invoke_transaction(invalid_invoke_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::InvalidTransactionNonce
))
);
}

#[rstest]
#[tokio::test]
async fn fail_if_insufficient_max_fee(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_invoke_transaction = BroadcastedInvokeTransaction {
sender_address: FieldElement::from_hex_be("valid_address").unwrap(),
calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()],
max_fee: FieldElement::from_hex_be("0x000000").unwrap(), //here max_fee is insufficient
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x01").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_invoke_transaction(invalid_invoke_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::InsufficientMaxFee
))
);
}

#[rstest]
#[tokio::test]
async fn fail_if_bad_calldata(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_invoke_transaction = BroadcastedInvokeTransaction {
sender_address: FieldElement::from_hex_be("valid_address").unwrap(),
calldata: vec![FieldElement::from_hex_be("0x000000").unwrap()], //here calldata is invalid
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x01").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_invoke_transaction(invalid_invoke_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::ValidationFailure
))
);
}

#[rstest]
#[tokio::test]
async fn works_ok_with_valid_params(deoxys: JsonRpcClient<HttpTransport>) {
let valid_invoke_transaction = BroadcastedInvokeTransaction {
sender_address: FieldElement::from_hex_be("valid_address").unwrap(),
calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()],
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x01").unwrap(),
is_query: false,
};

//Here we added a valid transaction
let response_deoxys = deoxys
.add_invoke_transaction(valid_invoke_transaction)
.await;

//Now, if the transaction is valid, the rpc call response contain the transaction hash
let transaction_submitted_hash = response_deoxys
.expect("Transaction submition failed")
.transaction_hash;

//Wait for the transaction to be added to the chain
thread::sleep(Duration::from_secs(15));

//Let's check the transaction status
let transaction_status = deoxys
.get_transaction_status(transaction_submitted_hash)
.await;

assert_matches!(transaction_status.unwrap(), TransactionStatus::Received);
}
159 changes: 159 additions & 0 deletions unit_tests/tests/test_deploy_account_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#![feature(assert_matches)]

mod common;
use common::*;
use starknet_core::types::{
BroadcastedDeployAccountTransaction, FieldElement, StarknetError, TransactionStatus,
};
use starknet_providers::{
jsonrpc::{HttpTransport, JsonRpcClient},
Provider, ProviderError,
};
use std::assert_matches::assert_matches;
use std::thread;
use std::time::Duration;

/// Test for the `deploy_account_transaction` Deoxys RPC method
/// Submit a new deploy account transaction
///
/// There is two type of DeployAccountTransaction: V1 and V3
///
/// # Arguments
/// * `deploy_account_transaction` - A deploy account transaction
/// with following fields (V1):
/// * `type` - DEPLOY_ACCOUNT
/// * `max_fee` - The maximal fee willing to be paid
/// * `signature` - The transaction signature
/// * `nonce` - The nonce of the transaction
/// * `contract_address_salt` - The salt for the address of the deployed contract
/// * `constructor_calldata` - The parameters passed to the constructor
/// * `class_hash` - The hash of the deployed contract's class
/// * `is_query` - If set to `true`, uses a query-only transaction version that's invalid for execution
///
/// # Returns
/// * `result` - The result of the transaction submission
/// with following fields:
/// * `transaction_hash` - The hash of the transaction
/// * `contract_address` - The address of the deployed contract
///
/// # Errors
/// * `invalid_transaction_nonce` - If the transaction nonce is invalid
/// * `insufficient_account_balance` - If the account balance is insufficient
/// * `insufficient_max_fee` - If the max fee is insufficient
/// * `invalid_transaction_nonce` - If the transaction nonce is invalid
/// * `validation_failure` - If the transaction validation fails
/// * `non_account` - If the sender address is not a valid account
/// * `duplicate_transaction` - If a transaction with same params already exists
/// * `unsupported_transaction_version` - If the transaction version is not supported
/// * `unexpected_error` - If an unexpected error occurs

#[rstest]
#[tokio::test]
async fn fail_if_param_(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction {
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid
contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(),
constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()],
class_hash: FieldElement::from_hex_be("0x000000").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_deploy_account_transaction(invalid_deploy_account_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::InvalidTransactionNonce
))
);
}

#[rstest]
#[tokio::test]
async fn fail_if_insufficient_max_fee(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction {
max_fee: FieldElement::from_hex_be("0x000000").unwrap(), //here max_fee is insufficient
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x000000").unwrap(),
contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(),
constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()],
class_hash: FieldElement::from_hex_be("0x000000").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_deploy_account_transaction(invalid_deploy_account_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::InsufficientMaxFee
))
);
}

#[rstest]
#[tokio::test]
async fn fail_if_invalid_transaction_nonce(deoxys: JsonRpcClient<HttpTransport>) {
let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction {
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid
contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(),
constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()],
class_hash: FieldElement::from_hex_be("0x000000").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_deploy_account_transaction(invalid_deploy_account_transaction)
.await;

assert_matches!(
response_deoxys,
Err(ProviderError::StarknetError(
StarknetError::InvalidTransactionNonce
))
);
}

#[rstest]
#[tokio::test]
async fn works_ok(deoxys: JsonRpcClient<HttpTransport>) {
let valid_deploy_account_transaction = BroadcastedDeployAccountTransaction {
max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(),
signature: vec![FieldElement::from_hex_be("signature_array").unwrap()],
nonce: FieldElement::from_hex_be("0x000000").unwrap(),
contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(),
constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()],
class_hash: FieldElement::from_hex_be("0x000000").unwrap(),
is_query: false,
};

let response_deoxys = deoxys
.add_deploy_account_transaction(valid_deploy_account_transaction)
.await;

//Here, as response we got the transaction hash and the contract address deployed
let result = response_deoxys.unwrap();

//Now, if the transaction is valid, the rpc call response contain the transaction hash
let transaction_submitted_hash = response_deoxys
.expect("Transaction submition failed")
.transaction_hash;

//Wait for the transaction to be added to the chain
thread::sleep(Duration::from_secs(15));

//Let's check the transaction status
let transaction_status = deoxys
.get_transaction_status(transaction_submitted_hash)
.await;

assert_matches!(transaction_status.unwrap(), TransactionStatus::Received);
}
71 changes: 71 additions & 0 deletions unit_tests/tests/test_trace_block_transactions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#![feature(assert_matches)]

mod common;
use common::*;

use rand::Rng;
use std::assert_matches::assert_matches;

use starknet_core::types::{BlockId, FieldElement, StarknetError};
use starknet_providers::{
jsonrpc::{HttpTransport, JsonRpcClient},
Provider, ProviderError,
};

#[rstest]
#[tokio::test]
async fn fail_non_existing_block(deoxys: JsonRpcClient<HttpTransport>) {
assert_matches!(
deoxys
.trace_block_transactions(BlockId::Hash(FieldElement::ZERO))
.await,
Err(ProviderError::StarknetError(StarknetError::BlockNotFound))
);
}

#[rstest]
#[tokio::test]
async fn works_ok_for_block_10000(
deoxys: JsonRpcClient<HttpTransport>,
pathfinder: JsonRpcClient<HttpTransport>,
) {
let block_number = BlockId::Number(10000);

let deoxys_trace = deoxys.trace_block_transactions(block_number).await;
let _pathfinder_trace = pathfinder.trace_block_transactions(block_number).await;

assert_matches!(deoxys_trace, _pathfinder_trace);
}

#[rstest]
#[tokio::test]
async fn works_ok_for_block_300000(
deoxys: JsonRpcClient<HttpTransport>,
pathfinder: JsonRpcClient<HttpTransport>,
) {
let block_number = BlockId::Number(300000);

let deoxys_trace = deoxys.trace_block_transactions(block_number).await;
let _pathfinder_trace = pathfinder.trace_block_transactions(block_number).await;

assert_matches!(deoxys_trace, _pathfinder_trace);
}

#[rstest]
#[tokio::test]
async fn works_ok_for_random_block(
deoxys: JsonRpcClient<HttpTransport>,
pathfinder: JsonRpcClient<HttpTransport>,
) {
let mut rng = rand::thread_rng();
let random_block_number = rng.gen_range(100000..602000);

let block_number = BlockId::Number(random_block_number);

let deoxys_trace = deoxys.trace_block_transactions(block_number).await;
let _pathfinder_trace = pathfinder.trace_block_transactions(block_number).await;
println!("{:?}", deoxys_trace);
println!("block choose is: {:?}", block_number);

assert_matches!(deoxys_trace, _pathfinder_trace);
}
Loading