Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(gateway): user transaction improvements - hex serialization & query-only removal #436

Merged
merged 6 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

## Next release

- refactor(gateway): remove query-only support from user transactions
- fix(gateway): serialize `tip` as hex in UserTransaction
- fix(block_production): dynamic block closing now adds special address with prev block hash
- fix(rpc): call, simulate, estimate rpcs executed on top of the block, not at the start of it
- fix(compilation): crate-level compilation
Expand Down
19 changes: 13 additions & 6 deletions crates/madara/client/gateway/server/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use mc_rpc::{
};
use mp_block::{BlockId, BlockTag, MadaraBlock, MadaraMaybePendingBlockInfo, MadaraPendingBlock};
use mp_class::{ClassInfo, ContractClass};
use mp_gateway::error::StarknetError;
use mp_gateway::error::{StarknetError, StarknetErrorCode};
use mp_gateway::user_transaction::{
UserDeclareTransaction, UserDeployAccountTransaction, UserInvokeFunctionTransaction, UserTransaction,
AddTransactionResult, UserDeclareTransaction, UserDeployAccountTransaction, UserInvokeFunctionTransaction,
UserTransaction,
};
use mp_gateway::{
block::{BlockStatus, ProviderBlock, ProviderBlockPending, ProviderBlockSignature},
Expand Down Expand Up @@ -349,10 +350,16 @@ async fn declare_transaction(
tx: UserDeclareTransaction,
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Response<String> {
let tx: BroadcastedDeclareTxn<Felt> = tx.try_into().unwrap();
let tx: BroadcastedDeclareTxn<Felt> = match tx.try_into() {
Ok(tx) => tx,
Err(e) => {
let error = StarknetError::new(StarknetErrorCode::InvalidContractDefinition, e.to_string());
return create_json_response(hyper::StatusCode::OK, &error);
}
};

match add_transaction_provider.add_declare_transaction(tx).await {
Ok(result) => create_json_response(hyper::StatusCode::OK, &result),
Ok(result) => create_json_response(hyper::StatusCode::OK, &AddTransactionResult::from(result)),
Err(e) => create_json_response(hyper::StatusCode::OK, &e),
}
}
Expand All @@ -362,7 +369,7 @@ async fn deploy_account_transaction(
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Response<String> {
match add_transaction_provider.add_deploy_account_transaction(tx.into()).await {
Ok(result) => create_json_response(hyper::StatusCode::OK, &result),
Ok(result) => create_json_response(hyper::StatusCode::OK, &AddTransactionResult::from(result)),
Err(e) => create_json_response(hyper::StatusCode::OK, &e),
}
}
Expand All @@ -372,7 +379,7 @@ async fn invoke_transaction(
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Response<String> {
match add_transaction_provider.add_invoke_transaction(tx.into()).await {
Ok(result) => create_json_response(hyper::StatusCode::OK, &result),
Ok(result) => create_json_response(hyper::StatusCode::OK, &AddTransactionResult::from(result)),
Err(e) => create_json_response(hyper::StatusCode::OK, &e),
}
}
7 changes: 5 additions & 2 deletions crates/madara/primitives/class/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,11 @@ impl TryFrom<FlattenedSierraClass> for CompressedSierraClass {
serde_json::to_writer(&mut gzip_encoder, &flattened_sierra_class.sierra_program)?;
gzip_encoder.try_finish()?;
drop(gzip_encoder);
Trantorian1 marked this conversation as resolved.
Show resolved Hide resolved
base64_encoder.finish().map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "base64 encoding error"))?;
let sierra_program = String::from_utf8(base64_encoder.into_inner()).unwrap();
let encoded_data = base64_encoder
.finish()
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "base64 encoding error"))?;
let sierra_program = String::from_utf8(encoded_data)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "base64 encoding error: invalid utf8"))?;

Ok(Self {
sierra_program,
Expand Down
55 changes: 49 additions & 6 deletions crates/madara/primitives/gateway/src/user_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! This module handles user transactions that are sent to or received by the gateway.
//! It defines the structure and conversion logic for different types of user transactions.
//!
//! //! # Important Note
//! # Important Note
//!
//! Query-only transactions are intentionally not supported in this module. This is because
//! UserTransactions are specifically designed for transactions that are meant to be added
Expand All @@ -27,9 +27,11 @@
//!
//! The module defines [`UserTransactionConversionError`] for handling conversion failures:
//!
//! - `UnsupportedQueryTransaction`: When attempting to convert a query-only transaction
//! - `ContractClassDecodeError`: When contract class decoding fails
//! - [`UnsupportedQueryTransaction`]: When attempting to convert a query-only transaction
//! - [`ContractClassDecodeError`]: When contract class decoding fails
//!
//! [`UnsupportedQueryTransaction`]: UserTransactionConversionError::UnsupportedQueryTransaction
//! [`ContractClassDecodeError`]: UserTransactionConversionError::ContractClassDecodeError

use mp_class::{CompressedLegacyContractClass, CompressedSierraClass, FlattenedSierraClass};
use mp_convert::hex_serde::U64AsHex;
Expand All @@ -38,11 +40,52 @@ use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use starknet_types_core::felt::Felt;
use starknet_types_rpc::{
BroadcastedDeclareTxn, BroadcastedDeclareTxnV1, BroadcastedDeclareTxnV2, BroadcastedDeclareTxnV3,
BroadcastedDeployAccountTxn, BroadcastedInvokeTxn, BroadcastedTxn, DeployAccountTxnV1, DeployAccountTxnV3,
InvokeTxnV0, InvokeTxnV1, InvokeTxnV3,
AddInvokeTransactionResult, BroadcastedDeclareTxn, BroadcastedDeclareTxnV1, BroadcastedDeclareTxnV2,
BroadcastedDeclareTxnV3, BroadcastedDeployAccountTxn, BroadcastedInvokeTxn, BroadcastedTxn,
ClassAndTxnHash as AddDeclareTransactionResult, ContractAndTxnHash as AddDeployAccountTransactionResult,
DeployAccountTxnV1, DeployAccountTxnV3, InvokeTxnV0, InvokeTxnV1, InvokeTxnV3,
};

/// Gateway response when a transaction is successfully added to the mempool.
/// Generic type T represents the specific transaction result type
/// (Invoke, Declare, or DeployAccount).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddTransactionResult<T> {
/// Status code indicating the transaction was received successfully
code: AddTransactionCode,
/// The transaction result
#[serde(flatten)]
result: T,
}

/// Marker trait to restrict which types can be used as transaction results.
/// Only valid Starknet transaction responses implement this trait.
pub trait ValidTransactionResult {}

// Implement the marker trait for the three valid transaction result types
impl<T> ValidTransactionResult for AddInvokeTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeclareTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeployAccountTransactionResult<T> {}
Comment on lines +61 to +68
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider sealing this trait by either making it private if does not need to be accessed outside of this crate, or a sub-trait of a private trait if it does.

Suggested change
/// Marker trait to restrict which types can be used as transaction results.
/// Only valid Starknet transaction responses implement this trait.
pub trait ValidTransactionResult {}
// Implement the marker trait for the three valid transaction result types
impl<T> ValidTransactionResult for AddInvokeTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeclareTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeployAccountTransactionResult<T> {}
/// Ensures [ValidTransactionResult] is accessible from outside the crate but cannot be implemented on any new structs
trait Sealed {}
/// Marker trait to restrict which types can be used as transaction results.
/// Only valid Starknet transaction responses implement this trait.
pub trait ValidTransactionResult: Sealed {}
// Implement the marker trait for the three valid transaction result types
impl<T> Sealed for AddInvokeTransactionResult<T> {}
impl<T> Sealed for AddDeclareTransactionResult<T> {}
impl<T> Sealed for AddDeployAccountTransactionResult<T> {}
impl<T> ValidTransactionResult for AddInvokeTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeclareTransactionResult<T> {}
impl<T> ValidTransactionResult for AddDeployAccountTransactionResult<T> {}


// Conversion from a valid transaction result into a gateway response.
/// This ensures the response can only be created from permitted transaction types.
impl<R> From<R> for AddTransactionResult<R>
where
R: ValidTransactionResult,
{
fn from(result: R) -> Self {
Self { code: AddTransactionCode::TransactionReceived, result }
}
}

/// Status code used in the gateway response.
/// Currently only indicates successful addition to mempool.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AddTransactionCode {
TransactionReceived,
}

#[derive(Debug, thiserror::Error)]
pub enum UserTransactionConversionError {
#[error("User transaction can't be a query only transaction")]
Expand Down
Loading