Skip to content

Commit

Permalink
feat(starknet_gateway): use deploy account exists to skip validate
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Jan 9, 2025
1 parent 8f9b3c3 commit bc7634b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 30 deletions.
63 changes: 56 additions & 7 deletions crates/starknet_gateway/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ use std::sync::Arc;

use blockifier::context::ChainInfo;
use papyrus_network_types::network_types::BroadcastedMessageMetadata;
use starknet_api::core::Nonce;
use starknet_api::executable_transaction::AccountTransaction;
use starknet_api::rpc_transaction::RpcTransaction;
use starknet_api::transaction::TransactionHash;
use starknet_gateway_types::errors::GatewaySpecError;
use starknet_mempool_types::communication::{AddTransactionArgsWrapper, SharedMempoolClient};
use starknet_mempool_types::communication::{
AddTransactionArgsWrapper,
MempoolClientError,
SharedMempoolClient,
};
use starknet_mempool_types::mempool_types::{AccountState, AddTransactionArgs};
use starknet_sequencer_infra::component_definitions::ComponentStarter;
use starknet_sierra_compile::config::SierraToCasmCompilationConfig;
use starknet_state_sync_types::communication::SharedStateSyncClient;
use tracing::{error, info, instrument, Span};
use starknet_types_core::felt::Felt;
use tracing::{error, info, instrument, warn, Span};

use crate::compilation::GatewayCompiler;
use crate::config::GatewayConfig;
Expand Down Expand Up @@ -70,7 +76,7 @@ impl Gateway {
// Run the blocking task in the current span.
let curr_span = Span::current();
let add_tx_args =
tokio::task::spawn_blocking(move || curr_span.in_scope(|| blocking_task.process_tx()))
tokio::spawn(async move { curr_span.in_scope(|| blocking_task.process_tx()).await })
.await
.map_err(|join_err| {
error!("Failed to process tx: {}", join_err);
Expand All @@ -93,6 +99,7 @@ struct ProcessTxBlockingTask {
stateful_tx_validator: Arc<StatefulTransactionValidator>,
state_reader_factory: Arc<dyn StateReaderFactory>,
gateway_compiler: Arc<GatewayCompiler>,
mempool_client: SharedMempoolClient,
chain_info: ChainInfo,
tx: RpcTransaction,
}
Expand All @@ -104,12 +111,13 @@ impl ProcessTxBlockingTask {
stateful_tx_validator: gateway.stateful_tx_validator.clone(),
state_reader_factory: gateway.state_reader_factory.clone(),
gateway_compiler: gateway.gateway_compiler.clone(),
mempool_client: gateway.mempool_client.clone(),
chain_info: gateway.chain_info.clone(),
tx,
}
}

fn process_tx(self) -> GatewayResult<AddTransactionArgs> {
async fn process_tx(self) -> GatewayResult<AddTransactionArgs> {
// TODO(Arni, 1/5/2024): Perform congestion control.

// Perform stateless validations.
Expand All @@ -132,15 +140,21 @@ impl ProcessTxBlockingTask {
.stateful_tx_validator
.instantiate_validator(self.state_reader_factory.as_ref(), &self.chain_info)?;
let address = executable_tx.contract_address();
let nonce = validator.get_nonce(address).map_err(|e| {
let account_nonce = validator.get_nonce(address).map_err(|e| {
error!("Failed to get nonce for sender address {}: {}", address, e);
GatewaySpecError::UnexpectedError { data: "Internal server error.".to_owned() }
})?;

self.stateful_tx_validator.run_validate(&executable_tx, nonce, validator)?;
let skip_validate =
skip_stateful_validations(&executable_tx, account_nonce, self.mempool_client.clone())
.await;
self.stateful_tx_validator.run_validate(&executable_tx, skip_validate, validator)?;

// TODO(Arni): Add the Sierra and the Casm to the mempool input.
Ok(AddTransactionArgs { tx: executable_tx, account_state: AccountState { address, nonce } })
Ok(AddTransactionArgs {
tx: executable_tx,
account_state: AccountState { address, nonce: account_nonce },
})
}
}

Expand All @@ -157,3 +171,38 @@ pub fn create_gateway(
}

impl ComponentStarter for Gateway {}

// Check if validation of an invoke transaction should be skipped due to deploy_account not being
// processed yet. This feature is used to improve UX for users sending deploy_account + invoke at
// once.
async fn skip_stateful_validations(
tx: &AccountTransaction,
account_nonce: Nonce,
mempool_client: SharedMempoolClient,
) -> bool {
if let AccountTransaction::Invoke(tx) = tx {
// check if the transaction nonce is 1, meaning it is post deploy_account, and the
// account nonce is zero, meaning the account was not deployed yet.
if tx.nonce() == Nonce(Felt::ONE) && account_nonce == Nonce(Felt::ZERO) {
// We verify that a deploy_account transaction exists for this account. It is sufficient
// to check if the account exists in the mempool since it means that either it has a
// deploy_account transaction or transactions with future nonces that passed
// validations.
let result = mempool_client.has_tx_from_address(tx.sender_address()).await;
match result {
Ok(exists) => return exists,
Err(MempoolClientError::ClientError(_err)) => {
warn!("A client error occurred");
// Should we simply take a strict approach and in this case say the mempool did
// not find it?
return false;
}
Err(MempoolClientError::MempoolError(_err)) => {
panic!("No such error can occur on 'has_tx_from_address'")
}
}
}
}

false
}
25 changes: 2 additions & 23 deletions crates/starknet_gateway/src/stateful_transaction_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,8 @@ use blockifier::versioned_constants::VersionedConstants;
#[cfg(test)]
use mockall::automock;
use starknet_api::block::BlockInfo;
use starknet_api::core::Nonce;
use starknet_api::executable_transaction::{
AccountTransaction as ExecutableTransaction,
InvokeTransaction as ExecutableInvokeTransaction,
};
use starknet_api::executable_transaction::AccountTransaction as ExecutableTransaction;
use starknet_gateway_types::errors::GatewaySpecError;
use starknet_types_core::felt::Felt;
use tracing::error;

use crate::config::StatefulTransactionValidatorConfig;
Expand Down Expand Up @@ -58,10 +53,9 @@ impl StatefulTransactionValidator {
pub fn run_validate<V: StatefulTransactionValidatorTrait>(
&self,
executable_tx: &ExecutableTransaction,
account_nonce: Nonce,
skip_validate: bool,
mut validator: V,
) -> StatefulTransactionValidatorResult<()> {
let skip_validate = skip_stateful_validations(executable_tx, account_nonce);
let only_query = false;
let charge_fee = enforce_fee(executable_tx, only_query);
let execution_flags = ExecutionFlags { only_query, charge_fee, validate: !skip_validate };
Expand Down Expand Up @@ -101,21 +95,6 @@ impl StatefulTransactionValidator {
}
}

// Check if validation of an invoke transaction should be skipped due to deploy_account not being
// processed yet. This feature is used to improve UX for users sending deploy_account + invoke at
// once.
fn skip_stateful_validations(tx: &ExecutableTransaction, account_nonce: Nonce) -> bool {
match tx {
ExecutableTransaction::Invoke(ExecutableInvokeTransaction { tx, .. }) => {
// check if the transaction nonce is 1, meaning it is post deploy_account, and the
// account nonce is zero, meaning the account was not deployed yet. The mempool also
// verifies that the deploy_account transaction exists.
tx.nonce() == Nonce(Felt::ONE) && account_nonce == Nonce(Felt::ZERO)
}
ExecutableTransaction::DeployAccount(_) | ExecutableTransaction::Declare(_) => false,
}
}

pub fn get_latest_block_info(
state_reader_factory: &dyn StateReaderFactory,
) -> StatefulTransactionValidatorResult<BlockInfo> {
Expand Down

0 comments on commit bc7634b

Please sign in to comment.