From 87a399047dd22e7dca9d5c2ae1e322a2733aa2a1 Mon Sep 17 00:00:00 2001 From: Ammar Arif Date: Mon, 2 Dec 2024 16:02:36 -0500 Subject: [PATCH] feat(katana): implement more feeder gateway types (#2744) --- .../src/implementation/blockifier/utils.rs | 21 +- crates/katana/feeder-gateway/src/client.rs | 21 +- .../src/{types.rs => types/mod.rs} | 41 +- .../feeder-gateway/src/types/receipt.rs | 22 + .../feeder-gateway/src/types/transaction.rs | 410 ++++++++++++++++++ crates/katana/pool/src/tx.rs | 4 + crates/katana/primitives/src/block.rs | 1 - crates/katana/primitives/src/fee.rs | 3 + crates/katana/primitives/src/transaction.rs | 47 ++ .../katana/rpc/rpc-types/src/transaction.rs | 23 + 10 files changed, 580 insertions(+), 13 deletions(-) rename crates/katana/feeder-gateway/src/{types.rs => types/mod.rs} (71%) create mode 100644 crates/katana/feeder-gateway/src/types/receipt.rs create mode 100644 crates/katana/feeder-gateway/src/types/transaction.rs diff --git a/crates/katana/executor/src/implementation/blockifier/utils.rs b/crates/katana/executor/src/implementation/blockifier/utils.rs index 5762b7defe..cd2df55d33 100644 --- a/crates/katana/executor/src/implementation/blockifier/utils.rs +++ b/crates/katana/executor/src/implementation/blockifier/utils.rs @@ -31,7 +31,7 @@ use katana_cairo::cairo_vm::types::errors::program_errors::ProgramError; use katana_cairo::cairo_vm::vm::runners::cairo_runner::ExecutionResources; use katana_cairo::starknet_api::block::{BlockNumber, BlockTimestamp}; use katana_cairo::starknet_api::core::{ - self, ChainId, ClassHash, CompiledClassHash, ContractAddress, Nonce, + self, ChainId, ClassHash, CompiledClassHash, ContractAddress, EntryPointSelector, Nonce, }; use katana_cairo::starknet_api::data_availability::DataAvailabilityMode; use katana_cairo::starknet_api::deprecated_contract_class::EntryPointType; @@ -184,6 +184,25 @@ pub fn to_executor_tx(tx: ExecutableTxWithHash) -> Transaction { match tx.transaction { ExecutableTx::Invoke(tx) => match tx { + InvokeTx::V0(tx) => { + let calldata = tx.calldata; + let signature = tx.signature; + + Transaction::AccountTransaction(AccountTransaction::Invoke(InvokeTransaction { + tx: ApiInvokeTransaction::V0( + katana_cairo::starknet_api::transaction::InvokeTransactionV0 { + entry_point_selector: EntryPointSelector(tx.entry_point_selector), + contract_address: to_blk_address(tx.contract_address), + signature: TransactionSignature(signature), + calldata: Calldata(Arc::new(calldata)), + max_fee: Fee(tx.max_fee), + }, + ), + tx_hash: TransactionHash(hash), + only_query: false, + })) + } + InvokeTx::V1(tx) => { let calldata = tx.calldata; let signature = tx.signature; diff --git a/crates/katana/feeder-gateway/src/client.rs b/crates/katana/feeder-gateway/src/client.rs index 6177928298..78653e6fe0 100644 --- a/crates/katana/feeder-gateway/src/client.rs +++ b/crates/katana/feeder-gateway/src/client.rs @@ -57,7 +57,7 @@ impl SequencerGateway { block_id: BlockIdOrTag, ) -> Result { self.feeder_gateway("get_state_update") - .with_query_param("includeBlock", "true") + .add_query_param("includeBlock", "true") .with_block_id(block_id) .send() .await @@ -69,7 +69,7 @@ impl SequencerGateway { block_id: BlockIdOrTag, ) -> Result { self.feeder_gateway("get_class_by_hash") - .with_query_param("classHash", &format!("{hash:#x}")) + .add_query_param("classHash", &format!("{hash:#x}")) .with_block_id(block_id) .send() .await @@ -81,7 +81,7 @@ impl SequencerGateway { block_id: BlockIdOrTag, ) -> Result { self.feeder_gateway("get_compiled_class_by_class_hash") - .with_query_param("classHash", &format!("{hash:#x}")) + .add_query_param("classHash", &format!("{hash:#x}")) .with_block_id(block_id) .send() .await @@ -112,13 +112,13 @@ impl<'a> RequestBuilder<'a> { match block_id { // latest block is implied, if no block id specified BlockIdOrTag::Tag(BlockTag::Latest) => self, - BlockIdOrTag::Tag(BlockTag::Pending) => self.with_query_param("blockNumber", "pending"), - BlockIdOrTag::Hash(hash) => self.with_query_param("blockHash", &format!("{hash:#x}")), - BlockIdOrTag::Number(num) => self.with_query_param("blockNumber", &num.to_string()), + BlockIdOrTag::Tag(BlockTag::Pending) => self.add_query_param("blockNumber", "pending"), + BlockIdOrTag::Hash(hash) => self.add_query_param("blockHash", &format!("{hash:#x}")), + BlockIdOrTag::Number(num) => self.add_query_param("blockNumber", &num.to_string()), } } - fn with_query_param(mut self, key: &str, value: &str) -> Self { + fn add_query_param(mut self, key: &str, value: &str) -> Self { self.url.query_pairs_mut().append_pair(key, value); self } @@ -181,6 +181,7 @@ pub enum ErrorCode { #[cfg(test)] mod tests { + use super::*; #[test] @@ -214,9 +215,9 @@ mod tests { let req = RequestBuilder { client: &client, url: base_url }; let url = req - .with_query_param("param1", "value1") - .with_query_param("param2", "value2") - .with_query_param("param3", "value3") + .add_query_param("param1", "value1") + .add_query_param("param2", "value2") + .add_query_param("param3", "value3") .url; let query = url.query().unwrap(); diff --git a/crates/katana/feeder-gateway/src/types.rs b/crates/katana/feeder-gateway/src/types/mod.rs similarity index 71% rename from crates/katana/feeder-gateway/src/types.rs rename to crates/katana/feeder-gateway/src/types/mod.rs index d000acda75..d236ae3a67 100644 --- a/crates/katana/feeder-gateway/src/types.rs +++ b/crates/katana/feeder-gateway/src/types/mod.rs @@ -1,15 +1,25 @@ use std::collections::{BTreeMap, BTreeSet}; +use katana_primitives::block::{BlockHash, BlockNumber}; pub use katana_primitives::class::CasmContractClass; use katana_primitives::class::{ ClassHash, CompiledClassHash, LegacyContractClass, SierraContractClass, }; use katana_primitives::contract::{Nonce, StorageKey, StorageValue}; +use katana_primitives::da::L1DataAvailabilityMode; +use katana_primitives::version::ProtocolVersion; use katana_primitives::{ContractAddress, Felt}; use katana_rpc_types::class::ConversionError; pub use katana_rpc_types::class::RpcSierraContractClass; use serde::Deserialize; -use starknet::providers::sequencer::models::Block; +use starknet::core::types::ResourcePrice; +use starknet::providers::sequencer::models::BlockStatus; + +mod receipt; +mod transaction; + +pub use receipt::*; +pub use transaction::*; /// The contract class type returns by `/get_class_by_hash` endpoint. #[derive(Debug, Deserialize)] @@ -63,6 +73,35 @@ pub struct StateUpdateWithBlock { pub block: Block, } +// The reason why we're not using the GasPrices from the `katana_primitives` crate is because +// the serde impl is different. So for now, lets just use starknet-rs types. The type isn't +// that complex anyway so the conversion is simple. But if we can use the primitive types, we +// should. +#[derive(Debug, Deserialize)] +pub struct Block { + #[serde(default)] + pub block_hash: Option, + #[serde(default)] + pub block_number: Option, + pub parent_block_hash: BlockHash, + pub timestamp: u64, + pub sequencer_address: Option, + #[serde(default)] + pub state_root: Option, + #[serde(default)] + pub transaction_commitment: Option, + #[serde(default)] + pub event_commitment: Option, + pub status: BlockStatus, + pub l1_da_mode: L1DataAvailabilityMode, + pub l1_gas_price: ResourcePrice, + pub l1_data_gas_price: ResourcePrice, + pub transactions: Vec, + pub transaction_receipts: Vec, + #[serde(default)] + pub starknet_version: Option, +} + // -- Conversion to Katana primitive types. impl TryFrom for katana_primitives::class::ContractClass { diff --git a/crates/katana/feeder-gateway/src/types/receipt.rs b/crates/katana/feeder-gateway/src/types/receipt.rs new file mode 100644 index 0000000000..3969b85cf7 --- /dev/null +++ b/crates/katana/feeder-gateway/src/types/receipt.rs @@ -0,0 +1,22 @@ +use katana_primitives::receipt::{Event, MessageToL1}; +use katana_primitives::Felt; +use serde::Deserialize; +use starknet::providers::sequencer::models::{ + ExecutionResources, L1ToL2Message, TransactionExecutionStatus, +}; + +#[derive(Debug, Deserialize)] +pub struct ConfirmedReceipt { + pub transaction_hash: Felt, + pub transaction_index: u64, + #[serde(default)] + pub execution_status: Option, + #[serde(default)] + pub revert_error: Option, + #[serde(default)] + pub execution_resources: Option, + pub l1_to_l2_consumed_message: Option, + pub l2_to_l1_messages: Vec, + pub events: Vec, + pub actual_fee: Felt, +} diff --git a/crates/katana/feeder-gateway/src/types/transaction.rs b/crates/katana/feeder-gateway/src/types/transaction.rs new file mode 100644 index 0000000000..9425d3c10a --- /dev/null +++ b/crates/katana/feeder-gateway/src/types/transaction.rs @@ -0,0 +1,410 @@ +use katana_primitives::class::{ClassHash, CompiledClassHash}; +use katana_primitives::contract::Nonce; +use katana_primitives::fee::ResourceBoundsMapping; +use katana_primitives::transaction::{ + DeclareTx, DeclareTxV1, DeclareTxV2, DeclareTxV3, DeployAccountTx, DeployAccountTxV1, + DeployAccountTxV3, DeployTx, InvokeTx, InvokeTxV0, InvokeTxV1, InvokeTxV3, L1HandlerTx, Tx, + TxHash, TxWithHash, +}; +use katana_primitives::{ContractAddress, Felt}; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct ConfirmedTransaction { + #[serde(rename = "transaction_hash")] + pub hash: TxHash, + #[serde(flatten)] + pub tx: TypedTransaction, +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TypedTransaction { + Deploy(DeployTx), + Declare(RawDeclareTx), + L1Handler(L1HandlerTx), + InvokeFunction(RawInvokeTx), + DeployAccount(RawDeployAccountTx), +} + +// We redundantly define the `DataAvailabilityMode` enum here because the serde implementation is +// different from the one in the `katana_primitives` crate. And changing the serde implementation in +// the `katana_primitives` crate would break the database format. So, we have to define the type +// again. But see if we can remove it once we're okay with breaking the database format. +#[derive(Debug)] +pub enum DataAvailabilityMode { + L1, + L2, +} + +impl<'de> Deserialize<'de> for DataAvailabilityMode { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = u8::deserialize(deserializer)?; + match value { + 0 => Ok(DataAvailabilityMode::L1), + 1 => Ok(DataAvailabilityMode::L2), + _ => Err(serde::de::Error::custom(format!( + "Invalid data availability mode; expected 0 or 1 but got {value}" + ))), + } + } +} + +#[derive(Debug, Deserialize)] +pub struct RawInvokeTx { + // Alias for v0 transaction + #[serde(alias = "contract_address")] + pub sender_address: ContractAddress, + // v0 doesn't include nonce + #[serde(default)] + pub nonce: Option, + #[serde(default)] + pub entry_point_selector: Option, + pub calldata: Vec, + pub signature: Vec, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u128")] + pub max_fee: Option, + pub resource_bounds: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u64")] + pub tip: Option, + #[serde(default)] + pub paymaster_data: Option>, + #[serde(default)] + pub account_deployment_data: Option>, + #[serde(default)] + pub nonce_data_availability_mode: Option, + #[serde(default)] + pub fee_data_availability_mode: Option, + pub version: Felt, +} + +#[derive(Debug, Deserialize)] +pub struct RawDeclareTx { + pub sender_address: ContractAddress, + pub nonce: Felt, + pub signature: Vec, + pub class_hash: ClassHash, + pub compiled_class_hash: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u128")] + pub max_fee: Option, + pub resource_bounds: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u64")] + pub tip: Option, + #[serde(default)] + pub paymaster_data: Option>, + #[serde(default)] + pub account_deployment_data: Option>, + #[serde(default)] + pub nonce_data_availability_mode: Option, + #[serde(default)] + pub fee_data_availability_mode: Option, + pub version: Felt, +} + +#[derive(Debug, Deserialize)] +pub struct RawDeployAccountTx { + pub nonce: Nonce, + pub signature: Vec, + pub class_hash: ClassHash, + pub contract_address: ContractAddress, + pub contract_address_salt: Felt, + pub constructor_calldata: Vec, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u128")] + pub max_fee: Option, + #[serde(default)] + pub resource_bounds: Option, + #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_u64")] + pub tip: Option, + #[serde(default)] + pub paymaster_data: Option>, + #[serde(default)] + pub nonce_data_availability_mode: Option, + #[serde(default)] + pub fee_data_availability_mode: Option, + pub version: Felt, +} + +#[derive(Debug, thiserror::Error)] +pub enum TxTryFromError { + #[error("Unsupported transaction version {version:#x}")] + UnsupportedVersion { version: Felt }, + + #[error("Missing `tip`")] + MissingTip, + + #[error("Missing `paymaster_data`")] + MissingPaymasterData, + + #[error("Missing `entry_point_selector`")] + MissingEntryPointSelector, + + #[error("Missing `nonce`")] + MissingNonce, + + #[error("Missing `max_fee`")] + MissingMaxFee, + + #[error("Missing `resource_bounds`")] + MissingResourceBounds, + + #[error("Missing `account_deployment_data`")] + MissingAccountDeploymentData, + + #[error("Missing nonce `data_availability_mode`")] + MissingNonceDA, + + #[error("Missing fee `data_availability_mode`")] + MissingFeeDA, + + #[error("Missing `compiled_class_hash`")] + MissingCompiledClassHash, +} + +fn deserialize_optional_u128<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrNum { + String(String), + Number(u128), + } + + match Option::::deserialize(deserializer)? { + None => Ok(None), + Some(StringOrNum::Number(n)) => Ok(Some(n)), + Some(StringOrNum::String(s)) => { + if let Some(hex) = s.strip_prefix("0x") { + u128::from_str_radix(hex, 16).map(Some).map_err(serde::de::Error::custom) + } else { + s.parse().map(Some).map_err(serde::de::Error::custom) + } + } + } +} + +fn deserialize_optional_u64<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum StringOrNum { + String(String), + Number(u64), + } + + match Option::::deserialize(deserializer)? { + None => Ok(None), + Some(StringOrNum::Number(n)) => Ok(Some(n)), + Some(StringOrNum::String(s)) => { + if let Some(hex) = s.strip_prefix("0x") { + u64::from_str_radix(hex, 16).map(Some).map_err(serde::de::Error::custom) + } else { + s.parse().map(Some).map_err(serde::de::Error::custom) + } + } + } +} + +// -- Conversion to Katana primitive types. + +impl TryFrom for TxWithHash { + type Error = TxTryFromError; + + fn try_from(tx: ConfirmedTransaction) -> Result { + let transaction = match tx.tx { + TypedTransaction::Deploy(tx) => Tx::Deploy(tx), + TypedTransaction::Declare(tx) => Tx::Declare(DeclareTx::try_from(tx)?), + TypedTransaction::L1Handler(tx) => Tx::L1Handler(tx), + TypedTransaction::InvokeFunction(tx) => Tx::Invoke(InvokeTx::try_from(tx)?), + TypedTransaction::DeployAccount(tx) => { + Tx::DeployAccount(DeployAccountTx::try_from(tx)?) + } + }; + + Ok(TxWithHash { hash: tx.hash, transaction }) + } +} + +impl TryFrom for InvokeTx { + type Error = TxTryFromError; + + fn try_from(value: RawInvokeTx) -> Result { + if Felt::ZERO == value.version { + Ok(InvokeTx::V0(InvokeTxV0 { + calldata: value.calldata, + signature: value.signature, + contract_address: value.sender_address, + max_fee: value.max_fee.ok_or(TxTryFromError::MissingMaxFee)?, + entry_point_selector: value + .entry_point_selector + .ok_or(TxTryFromError::MissingEntryPointSelector)?, + })) + } else if Felt::ONE == value.version { + Ok(InvokeTx::V1(InvokeTxV1 { + chain_id: Default::default(), + nonce: value.nonce.ok_or(TxTryFromError::MissingNonce)?, + calldata: value.calldata, + signature: value.signature, + max_fee: value.max_fee.ok_or(TxTryFromError::MissingMaxFee)?, + sender_address: value.sender_address, + })) + } else if Felt::THREE == value.version { + let tip = value.tip.ok_or(TxTryFromError::MissingTip)?; + let paymaster_data = + value.paymaster_data.ok_or(TxTryFromError::MissingPaymasterData)?; + let resource_bounds = + value.resource_bounds.ok_or(TxTryFromError::MissingResourceBounds)?; + let account_deployment_data = value + .account_deployment_data + .ok_or(TxTryFromError::MissingAccountDeploymentData)?; + let nonce_data_availability_mode = + value.nonce_data_availability_mode.ok_or(TxTryFromError::MissingNonceDA)?; + let fee_data_availability_mode = + value.fee_data_availability_mode.ok_or(TxTryFromError::MissingFeeDA)?; + + Ok(InvokeTx::V3(InvokeTxV3 { + tip, + paymaster_data, + chain_id: Default::default(), + nonce: value.nonce.ok_or(TxTryFromError::MissingNonce)?, + calldata: value.calldata, + signature: value.signature, + sender_address: value.sender_address, + resource_bounds, + account_deployment_data, + fee_data_availability_mode: fee_data_availability_mode.into(), + nonce_data_availability_mode: nonce_data_availability_mode.into(), + })) + } else { + Err(TxTryFromError::UnsupportedVersion { version: value.version }) + } + } +} + +impl TryFrom for DeclareTx { + type Error = TxTryFromError; + + fn try_from(value: RawDeclareTx) -> Result { + if Felt::ONE == value.version { + Ok(DeclareTx::V1(DeclareTxV1 { + chain_id: Default::default(), + sender_address: value.sender_address, + nonce: value.nonce, + signature: value.signature, + class_hash: value.class_hash, + max_fee: value.max_fee.ok_or(TxTryFromError::MissingMaxFee)?, + })) + } else if Felt::TWO == value.version { + Ok(DeclareTx::V2(DeclareTxV2 { + chain_id: Default::default(), + sender_address: value.sender_address, + nonce: value.nonce, + signature: value.signature, + class_hash: value.class_hash, + compiled_class_hash: value + .compiled_class_hash + .ok_or(TxTryFromError::MissingCompiledClassHash)?, + max_fee: value.max_fee.ok_or(TxTryFromError::MissingMaxFee)?, + })) + } else if Felt::THREE == value.version { + let resource_bounds = + value.resource_bounds.ok_or(TxTryFromError::MissingResourceBounds)?; + let tip = value.tip.ok_or(TxTryFromError::MissingTip)?; + let paymaster_data = + value.paymaster_data.ok_or(TxTryFromError::MissingPaymasterData)?; + let account_deployment_data = value + .account_deployment_data + .ok_or(TxTryFromError::MissingAccountDeploymentData)?; + let nonce_data_availability_mode = + value.nonce_data_availability_mode.ok_or(TxTryFromError::MissingNonceDA)?; + let fee_data_availability_mode = + value.fee_data_availability_mode.ok_or(TxTryFromError::MissingFeeDA)?; + let compiled_class_hash = + value.compiled_class_hash.ok_or(TxTryFromError::MissingCompiledClassHash)?; + + Ok(DeclareTx::V3(DeclareTxV3 { + chain_id: Default::default(), + sender_address: value.sender_address, + nonce: value.nonce, + signature: value.signature, + class_hash: value.class_hash, + compiled_class_hash, + resource_bounds, + tip, + paymaster_data, + account_deployment_data, + nonce_data_availability_mode: nonce_data_availability_mode.into(), + fee_data_availability_mode: fee_data_availability_mode.into(), + })) + } else { + Err(TxTryFromError::UnsupportedVersion { version: value.version }) + } + } +} + +impl TryFrom for DeployAccountTx { + type Error = TxTryFromError; + + fn try_from(value: RawDeployAccountTx) -> Result { + if Felt::ONE == value.version { + Ok(DeployAccountTx::V1(DeployAccountTxV1 { + chain_id: Default::default(), + nonce: value.nonce, + signature: value.signature, + class_hash: value.class_hash, + contract_address: value.contract_address, + contract_address_salt: value.contract_address_salt, + constructor_calldata: value.constructor_calldata, + max_fee: value.max_fee.ok_or(TxTryFromError::MissingMaxFee)?, + })) + } else if Felt::THREE == value.version { + let resource_bounds = + value.resource_bounds.ok_or(TxTryFromError::MissingResourceBounds)?; + let tip = value.tip.ok_or(TxTryFromError::MissingTip)?; + let paymaster_data = + value.paymaster_data.ok_or(TxTryFromError::MissingPaymasterData)?; + let nonce_data_availability_mode = + value.nonce_data_availability_mode.ok_or(TxTryFromError::MissingNonceDA)?; + let fee_data_availability_mode = + value.fee_data_availability_mode.ok_or(TxTryFromError::MissingFeeDA)?; + + Ok(DeployAccountTx::V3(DeployAccountTxV3 { + chain_id: Default::default(), + nonce: value.nonce, + signature: value.signature, + class_hash: value.class_hash, + contract_address: value.contract_address, + contract_address_salt: value.contract_address_salt, + constructor_calldata: value.constructor_calldata, + resource_bounds, + tip, + paymaster_data, + nonce_data_availability_mode: nonce_data_availability_mode.into(), + fee_data_availability_mode: fee_data_availability_mode.into(), + })) + } else { + Err(TxTryFromError::UnsupportedVersion { version: value.version }) + } + } +} + +impl From for katana_primitives::da::DataAvailabilityMode { + fn from(mode: DataAvailabilityMode) -> Self { + match mode { + DataAvailabilityMode::L1 => Self::L1, + DataAvailabilityMode::L2 => Self::L2, + } + } +} diff --git a/crates/katana/pool/src/tx.rs b/crates/katana/pool/src/tx.rs index ca7117d292..f9bdd2100a 100644 --- a/crates/katana/pool/src/tx.rs +++ b/crates/katana/pool/src/tx.rs @@ -116,6 +116,7 @@ impl PoolTransaction for ExecutableTxWithHash { fn nonce(&self) -> Nonce { match &self.transaction { ExecutableTx::Invoke(tx) => match tx { + InvokeTx::V0(..) => unimplemented!("v0 transaction not supported"), InvokeTx::V1(v1) => v1.nonce, InvokeTx::V3(v3) => v3.nonce, }, @@ -135,6 +136,7 @@ impl PoolTransaction for ExecutableTxWithHash { fn sender(&self) -> ContractAddress { match &self.transaction { ExecutableTx::Invoke(tx) => match tx { + InvokeTx::V0(v1) => v1.contract_address, InvokeTx::V1(v1) => v1.sender_address, InvokeTx::V3(v3) => v3.sender_address, }, @@ -151,6 +153,7 @@ impl PoolTransaction for ExecutableTxWithHash { fn max_fee(&self) -> u128 { match &self.transaction { ExecutableTx::Invoke(tx) => match tx { + InvokeTx::V0(..) => 0, // V0 doesn't have max_fee InvokeTx::V1(v1) => v1.max_fee, InvokeTx::V3(_) => 0, // V3 doesn't have max_fee }, @@ -170,6 +173,7 @@ impl PoolTransaction for ExecutableTxWithHash { fn tip(&self) -> u64 { match &self.transaction { ExecutableTx::Invoke(tx) => match tx { + InvokeTx::V0(_) => 0, // V0 doesn't have tip InvokeTx::V1(_) => 0, // V1 doesn't have tip InvokeTx::V3(v3) => v3.tip, }, diff --git a/crates/katana/primitives/src/block.rs b/crates/katana/primitives/src/block.rs index f78982eaa6..12f47594cc 100644 --- a/crates/katana/primitives/src/block.rs +++ b/crates/katana/primitives/src/block.rs @@ -54,7 +54,6 @@ pub struct PartialHeader { pub protocol_version: ProtocolVersion, } -// TODO: change names to wei and fri /// The L1 gas prices. #[derive(Debug, Default, Clone, PartialEq, Eq)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] diff --git a/crates/katana/primitives/src/fee.rs b/crates/katana/primitives/src/fee.rs index b55a07d3f3..54d14e71e5 100644 --- a/crates/katana/primitives/src/fee.rs +++ b/crates/katana/primitives/src/fee.rs @@ -8,11 +8,14 @@ pub struct ResourceBounds { pub max_price_per_unit: u128, } +// Aliased to match the feeder gateway API #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ResourceBoundsMapping { + #[serde(alias = "L1_GAS")] pub l1_gas: ResourceBounds, + #[serde(alias = "L2_GAS")] pub l2_gas: ResourceBounds, } diff --git a/crates/katana/primitives/src/transaction.rs b/crates/katana/primitives/src/transaction.rs index 384961d8f3..92bf570de2 100644 --- a/crates/katana/primitives/src/transaction.rs +++ b/crates/katana/primitives/src/transaction.rs @@ -52,6 +52,7 @@ pub enum Tx { Declare(DeclareTx), L1Handler(L1HandlerTx), DeployAccount(DeployAccountTx), + Deploy(DeployTx), } #[derive(Debug)] @@ -155,10 +156,27 @@ impl DeclareTxWithClass { #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InvokeTx { + V0(InvokeTxV0), V1(InvokeTxV1), V3(InvokeTxV3), } +#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InvokeTxV0 { + /// The account address which the transaction is initiated from. + pub contract_address: ContractAddress, + /// Entry point selector + pub entry_point_selector: Felt, + /// The data used as the input to the execute entry point of sender account contract. + pub calldata: Vec, + /// The transaction signature associated with the sender address. + pub signature: Vec, + /// The max fee that the sender is willing to pay for the transaction. + pub max_fee: u128, +} + #[derive(Debug, Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -166,6 +184,7 @@ pub struct InvokeTxV1 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The account address which the transaction is initiated from. pub sender_address: ContractAddress, @@ -187,6 +206,7 @@ pub struct InvokeTxV3 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The account address which the transaction is initiated from. pub sender_address: ContractAddress, @@ -220,6 +240,10 @@ impl InvokeTx { /// Compute the hash of the transaction. pub fn calculate_hash(&self, is_query: bool) -> TxHash { match self { + InvokeTx::V0(..) => { + todo!() + } + InvokeTx::V1(tx) => compute_invoke_v1_tx_hash( Felt::from(tx.sender_address), &tx.calldata, @@ -274,6 +298,7 @@ pub struct DeclareTxV1 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The account address which the transaction is initiated from. pub sender_address: ContractAddress, @@ -296,6 +321,7 @@ pub struct DeclareTxV2 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The account address which the transaction is initiated from. pub sender_address: ContractAddress, @@ -320,6 +346,7 @@ pub struct DeclareTxV3 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The account address which the transaction is initiated from. pub sender_address: ContractAddress, @@ -396,6 +423,7 @@ pub struct L1HandlerTx { /// The L1 to L2 message nonce. pub nonce: Nonce, /// The chain id. + #[serde(default)] pub chain_id: ChainId, /// Amount of fee paid on L1. pub paid_fee_on_l1: u128, @@ -449,6 +477,7 @@ pub struct DeployAccountTxV1 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The nonce value of the account. Corresponds to the number of transactions initiated by /// sender. @@ -474,6 +503,7 @@ pub struct DeployAccountTxV3 { /// The chain id of the chain on which the transaction is initiated. /// /// Used as a simple replay attack protection. + #[serde(default)] pub chain_id: ChainId, /// The nonce value of the account. Corresponds to the number of transactions initiated by /// sender. @@ -534,6 +564,23 @@ impl DeployAccountTx { } } +/// Legacy Deploy transacation type. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct DeployTx { + /// The contract address of the account contract that will be deployed. + pub contract_address: Felt, + /// The salt used to generate the contract address. + pub contract_address_salt: Felt, + /// The input data to the constructor function of the contract class. + pub constructor_calldata: Vec, + /// The hash of the contract class from which the account contract will be deployed from. + pub class_hash: Felt, + /// Transaction version. + pub version: Felt, +} + #[derive(Debug, Clone, AsRef, Deref, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxWithHash { diff --git a/crates/katana/rpc/rpc-types/src/transaction.rs b/crates/katana/rpc/rpc-types/src/transaction.rs index 3d6ba5e467..76bae94db5 100644 --- a/crates/katana/rpc/rpc-types/src/transaction.rs +++ b/crates/katana/rpc/rpc-types/src/transaction.rs @@ -264,6 +264,19 @@ impl From for Tx { let transaction_hash = value.hash; let tx = match value.transaction { InternalTx::Invoke(invoke) => match invoke { + InvokeTx::V0(tx) => starknet::core::types::Transaction::Invoke( + starknet::core::types::InvokeTransaction::V0( + starknet::core::types::InvokeTransactionV0 { + transaction_hash, + calldata: tx.calldata, + signature: tx.signature, + max_fee: tx.max_fee.into(), + contract_address: tx.contract_address.into(), + entry_point_selector: tx.entry_point_selector, + }, + ), + ), + InvokeTx::V1(tx) => starknet::core::types::Transaction::Invoke( starknet::core::types::InvokeTransaction::V1( starknet::core::types::InvokeTransactionV1 { @@ -390,6 +403,16 @@ impl From for Tx { ), }) } + + InternalTx::Deploy(tx) => starknet::core::types::Transaction::Deploy( + starknet::core::types::DeployTransaction { + constructor_calldata: tx.constructor_calldata, + contract_address_salt: tx.contract_address_salt, + class_hash: tx.class_hash, + version: tx.version, + transaction_hash, + }, + ), }; Tx(tx)