Skip to content

Commit

Permalink
feat: create skelaton of transaction validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Mar 18, 2024
1 parent 39ac4d7 commit 4db9be8
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 0 deletions.
3 changes: 3 additions & 0 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ edition = "2021"
hyper = "0.13.9"
tokio = { version = "0.2", features = ["full"] }
thiserror = "1.0"
starknet_api = "0.8.0"
assert_matches = "1.5.0"
rstest = "0.17.0"

[lib]
name = "gateway_lib"
15 changes: 15 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
use starknet_api::transaction::TransactionVersion;
use thiserror::Error;

#[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))]
pub enum GatewayError {
#[error("Internal server error")]
InternalServerError,
#[error("Error while starting the server")]
ServerError,
#[error("Invalid transaction: {0}")]
TransactionValidatorError(#[from] TransactionValidatorError),
}

#[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))]
pub enum TransactionValidatorError {
#[error("Invalid transaction type")]
InvalidTransactionType,
#[error("Transactions of version {0:?} are not valid. {1}")]
InvalidTransactionVersion(TransactionVersion, String),
#[error("Blocked transaction version {0:?}. {1}")]
BlockedTransactionVersion(TransactionVersion, String),
}
1 change: 1 addition & 0 deletions crates/gateway/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod errors;
pub mod gateway;
pub mod transaction_validator;
91 changes: 91 additions & 0 deletions crates/gateway/src/transaction_validator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::errors::TransactionValidatorError;
use starknet_api::transaction::Transaction;
use starknet_api::transaction::TransactionVersion;

#[cfg(test)]
#[path = "transaction_validator_test.rs"]
mod transaction_validator_test;

#[derive(Default)]
pub struct TransactionValidatorConfig {
pub block_declare_cairo1: bool,
pub block_declare_cairo0: bool,

pub min_allowed_tx_version: usize,
pub max_allowed_tx_version: usize,
pub current_tx_version: usize, // Should this constant be a part of the config?
}

pub struct TransactionValidator {
pub config: TransactionValidatorConfig,
}

impl TransactionValidator {
pub fn validate(&self, tx: Transaction) -> Result<(), TransactionValidatorError> {
// Deploy transactions are deprecated.
// L1Handler transactions are not supported in the gateway.
if matches!(tx, Transaction::Deploy(_) | Transaction::L1Handler(_)) {
return Err(TransactionValidatorError::InvalidTransactionType);
}

// Check if the declaration of Cairo / Cairo-0 contracts are blocked.
if let Transaction::Declare(tx) = &tx {
if tx.version() < TransactionVersion::TWO && self.config.block_declare_cairo0 {
return Err(TransactionValidatorError::BlockedTransactionVersion(
tx.version(),
"Declare of Cairo 0 is blocked.".into(),
));
}
if tx.version() >= TransactionVersion::TWO && self.config.block_declare_cairo1 {
return Err(TransactionValidatorError::BlockedTransactionVersion(
tx.version(),
"Transaction type is temporarily blocked.".into(),
));
}
}

// TODO(Arni, 1/4/2024): Add a mechanism that validate the sender address is not blocked.
let version = get_tx_version(&tx);
let version_as_usize: usize = version.0.try_into().expect("Invalid version.");
if version_as_usize < self.config.min_allowed_tx_version {
return Err(TransactionValidatorError::InvalidTransactionVersion(
version,
format!(
"Minimal supported version is {}.",
self.config.min_allowed_tx_version
),
));
}
if version_as_usize > self.config.current_tx_version {
return Err(TransactionValidatorError::InvalidTransactionVersion(
version,
format!(
"Maximal valid version is {}.",
self.config.current_tx_version
),
));
}
if version_as_usize > self.config.max_allowed_tx_version {
return Err(TransactionValidatorError::InvalidTransactionVersion(
version,
format!(
"Maximal supported version is {}.",
self.config.max_allowed_tx_version
),
));
}

// TODO(Arni, 1/4/2024): Validate fee and tx size.
Ok(())
}
}

fn get_tx_version(tx: &Transaction) -> TransactionVersion {
match tx {
Transaction::Declare(tx) => tx.version(),
Transaction::DeployAccount(tx) => tx.version(),
Transaction::Invoke(tx) => tx.version(),
Transaction::Deploy(_) => TransactionVersion::ZERO,
Transaction::L1Handler(_) => TransactionVersion::ZERO,
}
}
100 changes: 100 additions & 0 deletions crates/gateway/src/transaction_validator_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use rstest::rstest;

use starknet_api::transaction::InvokeTransaction;
use starknet_api::transaction::InvokeTransactionV1;
use starknet_api::transaction::Transaction;
use starknet_api::transaction::TransactionVersion;

use crate::errors::TransactionValidatorError;
use crate::transaction_validator::{TransactionValidator, TransactionValidatorConfig};

#[rstest]
#[case::block_declare_on_cairo_version_0(
TransactionValidatorConfig {
block_declare_cairo0: true,
..Default::default()
},
Transaction::Declare(starknet_api::transaction::DeclareTransaction::V0(
starknet_api::transaction::DeclareTransactionV0V1 {..Default::default()}
)),
TransactionValidatorError::BlockedTransactionVersion(
TransactionVersion::ZERO,
"Declare of Cairo 0 is blocked.".to_string()
)
)]
#[case::block_declare_on_cairo_version_1(
TransactionValidatorConfig {
block_declare_cairo1: true,
..Default::default()
},
Transaction::Declare(starknet_api::transaction::DeclareTransaction::V2(
starknet_api::transaction::DeclareTransactionV2 {..Default::default()}
)),
TransactionValidatorError::BlockedTransactionVersion(
TransactionVersion::TWO,
"Transaction type is temporarily blocked.".to_string()
)
)]
#[case::tx_version_below_minimal(
TransactionValidatorConfig {
min_allowed_tx_version: 3,
max_allowed_tx_version: 3,
current_tx_version: 3,
..Default::default()
},
Transaction::Invoke(InvokeTransaction::V1(InvokeTransactionV1 {..Default::default()})),
TransactionValidatorError::InvalidTransactionVersion(
TransactionVersion::ONE,
"Minimal supported version is 3.".to_string()
)
)]
#[case::tx_version_above_maximal(
TransactionValidatorConfig {
min_allowed_tx_version: 0,
max_allowed_tx_version: 0,
current_tx_version: 1,
..Default::default()
},
Transaction::Invoke(InvokeTransaction::V1(InvokeTransactionV1 {..Default::default()})),
TransactionValidatorError::InvalidTransactionVersion(
TransactionVersion::ONE,
"Maximal supported version is 0.".to_string()
)
)]
#[case::tx_version_above_current(
TransactionValidatorConfig {
min_allowed_tx_version: 0,
max_allowed_tx_version: 0,
current_tx_version: 0,
..Default::default()
},
Transaction::Invoke(InvokeTransaction::V1(InvokeTransactionV1 {..Default::default()})),
TransactionValidatorError::InvalidTransactionVersion(
TransactionVersion::ONE,
"Maximal valid version is 0.".to_string()
)
)]
#[case::deprecated_deploy_tx(
TransactionValidatorConfig {
..Default::default()
},
Transaction::Deploy(starknet_api::transaction::DeployTransaction {..Default::default()}),
TransactionValidatorError::InvalidTransactionType
)]
#[case::unsupported_l1_handler_tx(
TransactionValidatorConfig {
..Default::default()
},
Transaction::L1Handler(starknet_api::transaction::L1HandlerTransaction {..Default::default()}),
TransactionValidatorError::InvalidTransactionType
)]
fn test_transaction_version(
#[case] config: TransactionValidatorConfig,
#[case] tx: Transaction,
#[case] expected_error: TransactionValidatorError,
) {
let tx_validator = TransactionValidator { config };
let result = tx_validator.validate(tx);

assert_eq!(result.unwrap_err(), expected_error);
}

0 comments on commit 4db9be8

Please sign in to comment.