Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.

Commit

Permalink
feat: transaction filter in runtime config (#1590)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored May 31, 2024
1 parent c81c5c5 commit 84b8b1b
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- feat: add `TransactionFilter<TxType>` to pallet-starknet `Config`
- chore: remove `ignore` from
`storage_changes_should_revert_on_transaction_revert` test
- dev: Implement tests for new rpc method starknet_getTransactionStatus
Expand Down
57 changes: 38 additions & 19 deletions crates/pallets/starknet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ use mp_block::{Block as StarknetBlock, Header as StarknetHeader};
use mp_chain_id::MADARA_CHAIN_ID;
use mp_digest_log::MADARA_ENGINE_ID;
use mp_felt::Felt252Wrapper;
use mp_starknet_inherent::{InherentError, InherentType, STARKNET_INHERENT_IDENTIFIER};
use mp_starknet_inherent::{InherentError, InherentType, L1GasPrices, STARKNET_INHERENT_IDENTIFIER};
use mp_storage::{StarknetStorageSchemaVersion, PALLET_STARKNET_SCHEMA};
use mp_transactions::execution::{
execute_l1_handler_transaction, run_non_revertible_transaction, run_revertible_transaction,
execute_l1_handler_transaction, run_non_revertible_transaction, run_revertible_transaction, TransactionFilter,
};
use mp_transactions::{get_transaction_nonce, get_transaction_sender_address};
use sp_runtime::traits::UniqueSaturatedInto;
Expand Down Expand Up @@ -118,7 +118,6 @@ macro_rules! log {

#[frame_support::pallet]
pub mod pallet {
use mp_starknet_inherent::L1GasPrices;

use super::*;

Expand All @@ -132,6 +131,12 @@ pub mod pallet {
pub trait Config: frame_system::Config {
/// The block time
type TimestampProvider: Time;
/// Custom transaction filter for Invoke txs
type InvokeTransactionFilter: TransactionFilter<InvokeTransaction>;
/// Custom transaction filter for Declare txs
type DeclareTransactionFilter: TransactionFilter<DeclareTransaction>;
/// Custom transaction filter for DeployAccount txs
type DeployAccountTransactionFilter: TransactionFilter<DeployAccountTransaction>;
/// A configuration for base priority of unsigned transactions.
///
/// This is exposed so that it can be tuned for particular runtime, when
Expand Down Expand Up @@ -519,10 +524,20 @@ pub mod pallet {

// Execute
let tx_execution_infos = match transaction.tx.version() {
TransactionVersion::ZERO => {
run_non_revertible_transaction(&transaction, &mut state, &block_context, true, charge_fee)
}
_ => run_revertible_transaction(&transaction, &mut state, &block_context, true, charge_fee),
TransactionVersion::ZERO => run_non_revertible_transaction::<_, _, T::InvokeTransactionFilter>(
&transaction,
&mut state,
&block_context,
true,
charge_fee,
),
_ => run_revertible_transaction::<_, _, T::InvokeTransactionFilter>(
&transaction,
&mut state,
&block_context,
true,
charge_fee,
),
}
.map_err(|e| {
log!(error, "Invoke transaction execution failed: {:?}", e);
Expand Down Expand Up @@ -576,12 +591,14 @@ pub mod pallet {
let charge_fee = !<T as Config>::DisableTransactionFee::get();

// Execute
let tx_execution_infos =
run_non_revertible_transaction(&transaction, &mut state, &Self::get_block_context(), true, charge_fee)
.map_err(|e| {
log!(error, "Declare transaction execution failed: {:?}", e);
Error::<T>::TransactionExecutionFailed
})?;
let tx_execution_infos = run_non_revertible_transaction::<_, _, T::DeclareTransactionFilter>(
&transaction,
&mut state,
&Self::get_block_context(),
true,
charge_fee,
)
.map_err(|_| Error::<T>::TransactionExecutionFailed)?;

Self::emit_and_store_tx_and_fees_events(
transaction.tx_hash(),
Expand Down Expand Up @@ -626,12 +643,14 @@ pub mod pallet {
let charge_fee = !<T as Config>::DisableTransactionFee::get();

// Execute
let tx_execution_infos =
run_non_revertible_transaction(&transaction, &mut state, &Self::get_block_context(), true, charge_fee)
.map_err(|e| {
log!(error, "Deploy account transaction execution failed: {:?}", e);
Error::<T>::TransactionExecutionFailed
})?;
let tx_execution_infos = run_non_revertible_transaction::<_, _, T::DeployAccountTransactionFilter>(
&transaction,
&mut state,
&Self::get_block_context(),
true,
charge_fee,
)
.map_err(|_| Error::<T>::TransactionExecutionFailed)?;

Self::emit_and_store_tx_and_fees_events(
transaction.tx_hash,
Expand Down
22 changes: 12 additions & 10 deletions crates/pallets/starknet/src/simulations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,30 +351,32 @@ impl<T: Config> Pallet<T> {
simulation_flags: &SimulationFlags,
) -> Result<TransactionExecutionInfo, TransactionExecutionError> {
match transaction {
AccountTransaction::Declare(tx) => run_non_revertible_transaction(
tx,
state,
block_context,
simulation_flags.validate,
simulation_flags.charge_fee,
),
AccountTransaction::DeployAccount(tx) => run_non_revertible_transaction(
AccountTransaction::Declare(tx) => run_non_revertible_transaction::<_, _, T::DeclareTransactionFilter>(
tx,
state,
block_context,
simulation_flags.validate,
simulation_flags.charge_fee,
),
AccountTransaction::DeployAccount(tx) => {
run_non_revertible_transaction::<_, _, T::DeployAccountTransactionFilter>(
tx,
state,
block_context,
simulation_flags.validate,
simulation_flags.charge_fee,
)
}
AccountTransaction::Invoke(tx) if tx.tx.version() == TransactionVersion::ZERO => {
run_non_revertible_transaction(
run_non_revertible_transaction::<_, _, T::InvokeTransactionFilter>(
tx,
state,
block_context,
simulation_flags.validate,
simulation_flags.charge_fee,
)
}
AccountTransaction::Invoke(tx) => run_revertible_transaction(
AccountTransaction::Invoke(tx) => run_revertible_transaction::<_, _, T::InvokeTransactionFilter>(
tx,
state,
block_context,
Expand Down
3 changes: 3 additions & 0 deletions crates/pallets/starknet/src/tests/mock/setup_mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ macro_rules! mock_runtime {
type ProtocolVersion = ProtocolVersion;
type ProgramHash = ProgramHash;
type ExecutionConstants = ExecutionConstants;
type InvokeTransactionFilter = ();
type DeclareTransactionFilter = ();
type DeployAccountTransactionFilter = ();
}

/// Run to block n.
Expand Down
6 changes: 3 additions & 3 deletions crates/pallets/starknet/src/transaction_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ impl<T: Config> Pallet<T> {

match transaction {
AccountTransaction::Declare(transaction) => {
Validate::perform_pre_validation_stage(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
Validate::perform_pre_validation_stage::<T::DeclareTransactionFilter>(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
}
AccountTransaction::DeployAccount(transaction) => {
Validate::perform_pre_validation_stage(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
Validate::perform_pre_validation_stage::<T::DeployAccountTransactionFilter>(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
}
AccountTransaction::Invoke(transaction) => {
Validate::perform_pre_validation_stage(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
Validate::perform_pre_validation_stage::<T::InvokeTransactionFilter>(transaction, &mut state, tx_context, string_nonce_checking, charge_fee)
}
}
// TODO: have more granular error mapping
Expand Down
58 changes: 40 additions & 18 deletions crates/primitives/transactions/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ use starknet_api::transaction::{Calldata, Fee, ResourceBounds, TransactionVersio

use super::SIMULATE_TX_VERSION_OFFSET;

pub trait TransactionFilter<T> {
fn is_valid(transaction: &T) -> bool;
}

impl<T> TransactionFilter<T> for () {
fn is_valid(_transaction: &T) -> bool {
true
}
}

pub struct BanInvokeV0TransactionRule;

impl TransactionFilter<InvokeTransaction> for BanInvokeV0TransactionRule {
fn is_valid(transaction: &InvokeTransaction) -> bool {
!matches!(transaction.tx, starknet_api::transaction::InvokeTransaction::V0(_))
}
}

pub struct BanDeclareV0TransactionRule;

impl TransactionFilter<DeclareTransaction> for BanDeclareV0TransactionRule {
fn is_valid(transaction: &DeclareTransaction) -> bool {
!matches!(transaction.tx, starknet_api::transaction::DeclareTransaction::V0(_))
}
}

/// Contains the execution configuration regarding transaction fee
/// activation, transaction fee charging, nonce validation, transaction
/// validation and the execution mode (query or not).
Expand Down Expand Up @@ -332,13 +358,8 @@ impl GetValidateEntryPointSelector for L1HandlerTransaction {
}

#[allow(clippy::too_many_arguments)]
pub trait Validate: GetValidateEntryPointSelector {
// Implement this to blacklist some transaction versions
fn validate_tx_version(&self) -> TransactionExecutionResult<()> {
Ok(())
}

fn validate(
pub trait Validate: Sized + GetValidateEntryPointSelector {
fn validate<F: TransactionFilter<Self>>(
&self,
state: &mut dyn State,
tx_context: Arc<TransactionContext>,
Expand All @@ -349,7 +370,7 @@ pub trait Validate: GetValidateEntryPointSelector {
strict_nonce_checking: bool,
) -> TransactionExecutionResult<Option<CallInfo>>;

fn perform_pre_validation_stage(
fn perform_pre_validation_stage<F: TransactionFilter<Self>>(
&self,
state: &mut dyn State,
tx_context: Arc<TransactionContext>,
Expand All @@ -375,7 +396,7 @@ impl<
+ TransactionInfoCreator,
> Validate for T
{
fn validate(
fn validate<F: TransactionFilter<T>>(
&self,
state: &mut dyn State,
tx_context: Arc<TransactionContext>,
Expand All @@ -386,7 +407,7 @@ impl<
strict_nonce_checking: bool,
) -> TransactionExecutionResult<Option<CallInfo>> {
// Check tx version, nonce and fee
self.perform_pre_validation_stage(state, tx_context.clone(), strict_nonce_checking, charge_fee)?;
self.perform_pre_validation_stage::<F>(state, tx_context.clone(), strict_nonce_checking, charge_fee)?;

// Run the actual `validate` entrypoint
if validate_tx {
Expand All @@ -396,15 +417,14 @@ impl<
}
}

fn perform_pre_validation_stage(
fn perform_pre_validation_stage<F: TransactionFilter<T>>(
&self,
state: &mut dyn State,
tx_context: Arc<TransactionContext>,
strict_nonce_checking: bool,
charge_fee: bool,
) -> TransactionExecutionResult<()> {
// Check that version of the Tx is supported by the network
self.validate_tx_version()?;
assert!(F::is_valid(self), "custom chain provided tx filter failed");

// Check if nonce has a correct value
Self::handle_nonce(state, &tx_context.tx_info, strict_nonce_checking)?;
Expand Down Expand Up @@ -545,7 +565,7 @@ impl<S: State + SetArbitraryNonce> SetArbitraryNonce for CachedState<S> {
}
}

pub fn run_non_revertible_transaction<T, S>(
pub fn run_non_revertible_transaction<T, S, F>(
transaction: &T,
state: &mut S,
block_context: &BlockContext,
Expand All @@ -554,6 +574,7 @@ pub fn run_non_revertible_transaction<T, S>(
) -> TransactionExecutionResult<TransactionExecutionInfo>
where
S: State,
F: TransactionFilter<T>,
T: GetTxType + Executable<S> + Validate + GetActualCostBuilder + TransactionInfoCreator,
{
let mut resources = ExecutionResources::default();
Expand All @@ -570,7 +591,7 @@ where
let mut execution_context = EntryPointExecutionContext::new_validate(tx_context.clone(), charge_fee)?;
execute_call_info =
transaction.run_execute(state, &mut resources, &mut execution_context, &mut remaining_gas)?;
validate_call_info = transaction.validate(
validate_call_info = transaction.validate::<F>(
state,
tx_context.clone(),
&mut resources,
Expand All @@ -581,7 +602,7 @@ where
)?;
} else {
let mut execution_context = EntryPointExecutionContext::new_invoke(tx_context.clone(), charge_fee)?;
validate_call_info = transaction.validate(
validate_call_info = transaction.validate::<F>(
state,
tx_context.clone(),
&mut resources,
Expand Down Expand Up @@ -632,7 +653,7 @@ where
Ok(tx_execution_info)
}

pub fn run_revertible_transaction<T, S>(
pub fn run_revertible_transaction<T, S, F>(
transaction: &T,
state: &mut S,
block_context: &BlockContext,
Expand All @@ -647,12 +668,13 @@ where
+ GetCalldataLen
+ TransactionInfoCreator,
S: State + SetArbitraryNonce,
F: TransactionFilter<T>,
{
let mut resources = ExecutionResources::default();
let mut remaining_gas = block_context.versioned_constants().tx_initial_gas();
let tx_context = Arc::new(block_context.to_tx_context(transaction));

let validate_call_info = transaction.validate(
let validate_call_info = transaction.validate::<F>(
state,
tx_context.clone(),
&mut resources,
Expand Down
3 changes: 3 additions & 0 deletions crates/runtime/src/pallets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ impl pallet_starknet::Config for Runtime {
type ProtocolVersion = ProtocolVersion;
type ProgramHash = ProgramHash;
type ExecutionConstants = ExecutionConstants;
type InvokeTransactionFilter = ();
type DeclareTransactionFilter = ();
type DeployAccountTransactionFilter = ();
}

/// --------------------------------------
Expand Down

0 comments on commit 84b8b1b

Please sign in to comment.