Skip to content

Commit

Permalink
Merge pull request #49 from Tbelleng/feat/release_ditto
Browse files Browse the repository at this point in the history
✨ Adding add_invoke_transaction and deploy_account_transaction RPC test
  • Loading branch information
antiyro authored Mar 11, 2024
2 parents 019c369 + e401f7c commit 0e20dc6
Show file tree
Hide file tree
Showing 5 changed files with 382 additions and 0 deletions.
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);
}

0 comments on commit 0e20dc6

Please sign in to comment.