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 all 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
23 changes: 16 additions & 7 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 All @@ -23,7 +24,7 @@ use mp_utils::service::ServiceContext;
use serde::Serialize;
use serde_json::json;
use starknet_types_core::felt::Felt;
use starknet_types_rpc::TraceBlockTransactionsResult;
use starknet_types_rpc::{BroadcastedDeclareTxn, TraceBlockTransactionsResult};

use super::{
error::{GatewayError, OptionExt, ResultExt},
Expand Down Expand Up @@ -349,8 +350,16 @@ async fn declare_transaction(
tx: UserDeclareTransaction,
add_transaction_provider: Arc<dyn AddTransactionProvider>,
) -> Response<String> {
match add_transaction_provider.add_declare_transaction(tx.into()).await {
Ok(result) => create_json_response(hyper::StatusCode::OK, &result),
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, &AddTransactionResult::from(result)),
Err(e) => create_json_response(hyper::StatusCode::OK, &e),
}
}
Expand All @@ -360,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 @@ -370,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),
}
}
10 changes: 10 additions & 0 deletions crates/madara/client/rpc/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::utils::display_internal_server_error;
use mc_db::MadaraStorageError;
use mp_gateway::error::{StarknetError, StarknetErrorCode};
use mp_gateway::user_transaction::UserTransactionConversionError;
use serde::Serialize;
use serde_json::json;
use starknet_api::StarknetApiError;
Expand Down Expand Up @@ -228,6 +229,15 @@ impl From<StarknetApiError> for StarknetRpcApiError {
}
}

impl From<UserTransactionConversionError> for StarknetRpcApiError {
fn from(err: UserTransactionConversionError) -> Self {
match err {
UserTransactionConversionError::ContractClassDecodeError(_) => StarknetRpcApiError::InvalidContractClass,
UserTransactionConversionError::UnsupportedQueryTransaction => StarknetRpcApiError::UnsupportedTxnVersion,
}
}
}

#[cfg_attr(test, derive(PartialEq, Eq))]
#[derive(Debug)]
pub enum StarknetWsApiError {
Expand Down
29 changes: 17 additions & 12 deletions crates/madara/client/rpc/src/providers/forward_to_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ impl AddTransactionProvider for ForwardToProvider {
) -> RpcResult<ClassAndTxnHash<Felt>> {
let sequencer_response = match self
.provider
.add_declare_transaction(
declare_transaction.try_into().map_err(|_| StarknetRpcApiError::InvalidContractClass)?,
)
.add_declare_transaction(declare_transaction.try_into().map_err(StarknetRpcApiError::from)?)
.await
{
Ok(response) => response,
Expand All @@ -53,14 +51,17 @@ impl AddTransactionProvider for ForwardToProvider {
&self,
deploy_account_transaction: BroadcastedDeployAccountTxn<Felt>,
) -> RpcResult<ContractAndTxnHash<Felt>> {
let sequencer_response =
match self.provider.add_deploy_account_transaction(deploy_account_transaction.into()).await {
Ok(response) => response,
Err(SequencerError::StarknetError(e)) => {
return Err(StarknetRpcApiError::from(e).into());
}
Err(e) => bail_internal_server_error!("Failed to add deploy account transaction to sequencer: {e}"),
};
let sequencer_response = match self
.provider
.add_deploy_account_transaction(deploy_account_transaction.try_into().map_err(StarknetRpcApiError::from)?)
.await
{
Ok(response) => response,
Err(SequencerError::StarknetError(e)) => {
return Err(StarknetRpcApiError::from(e).into());
}
Err(e) => bail_internal_server_error!("Failed to add deploy account transaction to sequencer: {e}"),
};

Ok(sequencer_response)
}
Expand All @@ -69,7 +70,11 @@ impl AddTransactionProvider for ForwardToProvider {
&self,
invoke_transaction: BroadcastedInvokeTxn<Felt>,
) -> RpcResult<AddInvokeTransactionResult<Felt>> {
let sequencer_response = match self.provider.add_invoke_transaction(invoke_transaction.into()).await {
let sequencer_response = match self
.provider
.add_invoke_transaction(invoke_transaction.try_into().map_err(StarknetRpcApiError::from)?)
.await
{
Ok(response) => response,
Err(SequencerError::StarknetError(e)) => {
return Err(StarknetRpcApiError::from(e).into());
Expand Down
9 changes: 5 additions & 4 deletions crates/madara/primitives/class/src/into_starknet_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
};

impl TryFrom<starknet_types_rpc::MaybeDeprecatedContractClass<Felt>> for ContractClass {
type Error = base64::DecodeError;
type Error = std::io::Error;

fn try_from(contract_class: starknet_types_rpc::MaybeDeprecatedContractClass<Felt>) -> Result<Self, Self::Error> {
match contract_class {
Expand Down Expand Up @@ -123,15 +123,16 @@ impl From<SierraEntryPoint> for starknet_types_rpc::SierraEntryPoint<Felt> {
}

impl TryFrom<starknet_types_rpc::DeprecatedContractClass<Felt>> for CompressedLegacyContractClass {
type Error = base64::DecodeError;
type Error = std::io::Error;

fn try_from(
compressed_legacy_contract_class: starknet_types_rpc::DeprecatedContractClass<Felt>,
) -> Result<Self, Self::Error> {
use base64::Engine;

let decoded_program =
base64::engine::general_purpose::STANDARD.decode(&compressed_legacy_contract_class.program)?;
let decoded_program = base64::engine::general_purpose::STANDARD
.decode(&compressed_legacy_contract_class.program)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;

Ok(CompressedLegacyContractClass {
program: decoded_program,
Expand Down
53 changes: 53 additions & 0 deletions crates/madara/primitives/class/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,59 @@ impl FlattenedSierraClass {
}
}

#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct CompressedSierraClass {
/// The gzipped compressed program as a base64 string.
pub sierra_program: String,
pub contract_class_version: String,
pub entry_points_by_type: EntryPointsByType,
pub abi: String,
}

impl TryFrom<FlattenedSierraClass> for CompressedSierraClass {
type Error = std::io::Error;

fn try_from(flattened_sierra_class: FlattenedSierraClass) -> Result<Self, Self::Error> {
let mut base64_encoder =
base64::write::EncoderWriter::new(Vec::new(), &base64::engine::general_purpose::STANDARD);
let mut gzip_encoder = flate2::write::GzEncoder::new(&mut base64_encoder, flate2::Compression::default());
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
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,
contract_class_version: flattened_sierra_class.contract_class_version,
entry_points_by_type: flattened_sierra_class.entry_points_by_type,
abi: flattened_sierra_class.abi,
})
}
}

impl TryFrom<CompressedSierraClass> for FlattenedSierraClass {
type Error = std::io::Error;

fn try_from(compressed_sierra_class: CompressedSierraClass) -> Result<Self, Self::Error> {
let string_reader = std::io::Cursor::new(compressed_sierra_class.sierra_program);
let base64_decoder =
base64::read::DecoderReader::new(string_reader, &base64::engine::general_purpose::STANDARD);
let gzip_decoder = flate2::read::GzDecoder::new(base64_decoder);
Copy link
Member

Choose a reason for hiding this comment

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

aaaaaah
you should look at
#459

also I had no idea compressed sierra was a thing? i thought only legacy classes were compressed
that's new right?

let sierra_program = serde_json::from_reader(gzip_decoder)?;

Ok(Self {
sierra_program,
contract_class_version: compressed_sierra_class.contract_class_version,
entry_points_by_type: compressed_sierra_class.entry_points_by_type,
abi: compressed_sierra_class.abi,
})
}
}

#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub struct EntryPointsByType {
Expand Down
Loading
Loading