Skip to content

Commit

Permalink
feat: check for invalid seirra version in stateless validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Jun 25, 2024
1 parent d3ba9d8 commit a10e6aa
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 3 deletions.
63 changes: 63 additions & 0 deletions crates/gateway/src/compiler_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use cairo_lang_starknet_classes::compiler_version::VersionId as CairoLangVersionId;
use starknet_api::hash::StarkFelt;
use thiserror::Error;
use validator::Validate;

#[derive(Debug, Error)]
#[cfg_attr(test, derive(PartialEq))]
pub enum VersionIdError {
#[error("{message}")]
InvalidVersion { message: String },
}

// TODO(Arni): Share this struct with the Cairo lang crate.
#[derive(Clone, Copy, Debug, Validate, PartialEq)]
pub struct VersionId {
pub major: usize,
pub minor: usize,
pub patch: usize,
}

impl From<&VersionId> for CairoLangVersionId {
fn from(version: &VersionId) -> Self {
CairoLangVersionId { major: version.major, minor: version.minor, patch: version.patch }
}
}

impl std::fmt::Display for VersionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
CairoLangVersionId::from(self).fmt(f)
}
}

impl VersionId {
pub fn from_sierra_program(sierra_program: &[StarkFelt]) -> Result<Self, VersionIdError> {
let sierra_program_length = sierra_program.len();

if sierra_program_length < 3 {
return Err(VersionIdError::InvalidVersion {
message: format!(
"Sierra program is too short. got program of length {} which is not long \
enough to hold the version field.",
sierra_program_length
),
});
}

fn get_version_component(
sierra_program: &[StarkFelt],
index: usize,
) -> Result<usize, VersionIdError> {
let felt = sierra_program[index];
felt.try_into().map_err(|_| VersionIdError::InvalidVersion {
message: format!("version contains a value that is out of range: {}", felt),
})
}

Ok(VersionId {
major: get_version_component(sierra_program, 0)?,
minor: get_version_component(sierra_program, 1)?,
patch: get_version_component(sierra_program, 2)?,
})
}
}
4 changes: 4 additions & 0 deletions crates/gateway/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use starknet_api::StarknetApiError;
use thiserror::Error;
use tokio::task::JoinError;

use crate::compiler_version::VersionIdError;

/// Errors directed towards the end-user, as a result of gateway requests.
#[derive(Debug, Error)]
pub enum GatewayError {
Expand Down Expand Up @@ -46,6 +48,8 @@ pub enum StatelessTransactionValidatorError {
(allowed length: {max_signature_length})."
)]
SignatureTooLong { signature_length: usize, max_signature_length: usize },
#[error(transparent)]
InvalidSierraVersion(#[from] VersionIdError),
#[error(
"Cannot declare contract class with bytecode size of {bytecode_size}; max allowed size: \
{max_bytecode_size}."
Expand Down
1 change: 1 addition & 0 deletions crates/gateway/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod compiler_version;
pub mod config;
pub mod errors;
pub mod gateway;
Expand Down
5 changes: 3 additions & 2 deletions crates/gateway/src/starknet_api_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ pub fn external_tx_for_testing(
) -> RPCTransaction {
match tx_type {
TransactionType::Declare => {
// Minimal contract class.
let contract_class = ContractClass {
sierra_program: vec![stark_felt!(1_u32); 3],
..ContractClass::default()
sierra_program: vec![stark_felt!(1_u32), stark_felt!(3_u32), stark_felt!(0_u32)],
..Default::default()
};
external_declare_tx(declare_tx_args!(resource_bounds, signature, contract_class))
}
Expand Down
13 changes: 13 additions & 0 deletions crates/gateway/src/stateless_transaction_validator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use starknet_api::hash::StarkFelt;
use starknet_api::rpc_transaction::{
RPCDeclareTransaction, RPCDeployAccountTransaction, RPCInvokeTransaction, RPCTransaction,
ResourceBoundsMapping,
};
use starknet_api::transaction::Resource;

use crate::compiler_version::VersionId;
use crate::config::StatelessTransactionValidatorConfig;
use crate::errors::{StatelessTransactionValidatorError, StatelessTransactionValidatorResult};

Expand Down Expand Up @@ -103,9 +105,20 @@ impl StatelessTransactionValidator {
let contract_class = match declare_tx {
RPCDeclareTransaction::V3(tx) => &tx.contract_class,
};
self.validate_sierra_version(&contract_class.sierra_program)?;
self.validate_class_length(contract_class)
}

fn validate_sierra_version(
&self,
sierra_program: &[StarkFelt],
) -> StatelessTransactionValidatorResult<()> {
// TODO(Arni): Validate the sierra version is supported.
let _sierra_version = VersionId::from_sierra_program(sierra_program)?;

Ok(())
}

fn validate_class_length(
&self,
contract_class: &starknet_api::rpc_transaction::ContractClass,
Expand Down
58 changes: 57 additions & 1 deletion crates/gateway/src/stateless_transaction_validator_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use starknet_api::rpc_transaction::{ContractClass, ResourceBoundsMapping};
use starknet_api::transaction::{Calldata, Resource, ResourceBounds, TransactionSignature};
use starknet_api::{calldata, stark_felt};

use crate::compiler_version::VersionIdError;
use crate::config::StatelessTransactionValidatorConfig;
use crate::declare_tx_args;
use crate::starknet_api_test_utils::{
Expand Down Expand Up @@ -184,6 +185,61 @@ fn test_signature_too_long(
);
}

#[rstest]
#[case::sierra_program_length_zero(
vec![],
StatelessTransactionValidatorError::InvalidSierraVersion (
VersionIdError::InvalidVersion {
message: "Sierra program is too short. got program of length 0 which is not long enough \
to hold the version field.".into()
}
)
)]
#[case::sierra_program_length_one(
vec![stark_felt!(1_u128)],
StatelessTransactionValidatorError::InvalidSierraVersion (
VersionIdError::InvalidVersion {
message: "Sierra program is too short. got program of length 1 which is not long enough \
to hold the version field.".into()
}
)
)]
#[case::sierra_program_length_two(
vec![stark_felt!(1_u128), stark_felt!(3_u128)],
StatelessTransactionValidatorError::InvalidSierraVersion (
VersionIdError::InvalidVersion {
message: "Sierra program is too short. got program of length 2 which is not long enough \
to hold the version field.".into()
}
)
)]
#[case::invalid_character_in_sierra_version(
vec![
stark_felt!(1_u128),
stark_felt!(3_u128),
stark_felt!(0x10000000000000000_u128), // Does not fit into a usize.
],
StatelessTransactionValidatorError::InvalidSierraVersion (
VersionIdError::InvalidVersion {
message: "version contains a value that is out of range: \
0x0000000000000000000000000000000000000000000000010000000000000000".into()
}
)
)
]
fn test_invalid_sierra_version(
#[case] sierra_program: Vec<StarkFelt>,
#[case] expected_error: StatelessTransactionValidatorError,
) {
let tx_validator =
StatelessTransactionValidator { config: DEFAULT_VALIDATOR_CONFIG_FOR_TESTING };

let contract_class = ContractClass { sierra_program, ..Default::default() };
let tx = external_declare_tx(declare_tx_args!(contract_class));

assert_eq!(tx_validator.validate(&tx).unwrap_err(), expected_error);
}

#[test]
fn test_declare_bytecode_size_too_long() {
let config_max_bytecode_size = 10;
Expand Down Expand Up @@ -211,7 +267,7 @@ fn test_declare_bytecode_size_too_long() {

#[test]
fn test_declare_contract_class_size_too_long() {
let config_max_raw_class_size = 100;
let config_max_raw_class_size = 100; // Some arbitrary value, which will fail the test.
let tx_validator = StatelessTransactionValidator {
config: StatelessTransactionValidatorConfig {
max_raw_class_size: config_max_raw_class_size,
Expand Down

0 comments on commit a10e6aa

Please sign in to comment.