diff --git a/src/external_transaction.rs b/src/external_transaction.rs deleted file mode 100644 index 14182429..00000000 --- a/src/external_transaction.rs +++ /dev/null @@ -1,153 +0,0 @@ -#[cfg(test)] -#[path = "external_transaction_test.rs"] -mod external_transaction_test; - -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; -use crate::data_availability::DataAvailabilityMode; -use crate::state::{EntryPoint, EntryPointType}; -use crate::transaction::{ - AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBoundsMapping, - Tip, TransactionSignature, -}; - -/// An external transaction. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(tag = "type")] -pub enum ExternalTransaction { - /// A declare transaction. - #[serde(rename = "DECLARE")] - Declare(ExternalDeclareTransaction), - /// A deploy account transaction. - #[serde(rename = "DEPLOY_ACCOUNT")] - DeployAccount(ExternalDeployAccountTransaction), - /// An invoke transaction. - #[serde(rename = "INVOKE_FUNCTION")] - Invoke(ExternalInvokeTransaction), -} - -macro_rules! implement_ref_getters { - ($(($member_name:ident, $member_type:ty)), *) => { - $(pub fn $member_name(&self) -> &$member_type { - match self { - ExternalTransaction::Declare( - ExternalDeclareTransaction::V3(tx) - ) => &tx.$member_name, - ExternalTransaction::DeployAccount( - ExternalDeployAccountTransaction::V3(tx) - ) => &tx.$member_name, - ExternalTransaction::Invoke( - ExternalInvokeTransaction::V3(tx) - ) => &tx.$member_name - } - })* - }; -} - -impl ExternalTransaction { - implement_ref_getters!( - (nonce, Nonce), - (resource_bounds, ResourceBoundsMapping), - (signature, TransactionSignature), - (tip, Tip) - ); -} - -/// A declare transaction that can be added to Starknet through the Starknet gateway. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(tag = "version")] -pub enum ExternalDeclareTransaction { - #[serde(rename = "0x3")] - V3(ExternalDeclareTransactionV3), -} - -/// A deploy account transaction that can be added to Starknet through the Starknet gateway. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(tag = "version")] -pub enum ExternalDeployAccountTransaction { - #[serde(rename = "0x3")] - V3(ExternalDeployAccountTransactionV3), -} - -/// An invoke transaction that can be added to Starknet through the Starknet gateway. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(tag = "version")] -pub enum ExternalInvokeTransaction { - #[serde(rename = "0x3")] - V3(ExternalInvokeTransactionV3), -} - -/// A declare transaction of a Cairo-v1 contract class that can be added to Starknet through the -/// Starknet gateway. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ExternalDeclareTransactionV3 { - pub contract_class: ContractClass, - pub resource_bounds: ResourceBoundsMapping, - pub tip: Tip, - pub signature: TransactionSignature, - pub nonce: Nonce, - pub compiled_class_hash: CompiledClassHash, - pub sender_address: ContractAddress, - pub nonce_data_availability_mode: DataAvailabilityMode, - pub fee_data_availability_mode: DataAvailabilityMode, - pub paymaster_data: PaymasterData, - pub account_deployment_data: AccountDeploymentData, -} - -/// A deploy account transaction that can be added to Starknet through the Starknet gateway. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ExternalDeployAccountTransactionV3 { - pub resource_bounds: ResourceBoundsMapping, - pub tip: Tip, - pub contract_address_salt: ContractAddressSalt, - pub class_hash: ClassHash, - pub constructor_calldata: Calldata, - pub nonce: Nonce, - pub signature: TransactionSignature, - pub nonce_data_availability_mode: DataAvailabilityMode, - pub fee_data_availability_mode: DataAvailabilityMode, - pub paymaster_data: PaymasterData, -} - -/// An invoke account transaction that can be added to Starknet through the Starknet gateway. -/// The invoke is a V3 transaction. -/// It has a serialization format that the Starknet gateway accepts in the `add_transaction` -/// HTTP method. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct ExternalInvokeTransactionV3 { - pub resource_bounds: ResourceBoundsMapping, - pub tip: Tip, - pub calldata: Calldata, - pub sender_address: ContractAddress, - pub nonce: Nonce, - pub signature: TransactionSignature, - pub nonce_data_availability_mode: DataAvailabilityMode, - pub fee_data_availability_mode: DataAvailabilityMode, - pub paymaster_data: PaymasterData, - pub account_deployment_data: AccountDeploymentData, -} - -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct ContractClass { - #[serde(rename = "sierra_program")] - pub compressed_sierra_program: String, - pub contract_class_version: String, - pub entry_points_by_type: HashMap>, - pub abi: String, -} diff --git a/src/lib.rs b/src/lib.rs index 2eab53ca..7196b3d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,9 @@ pub mod core; pub mod crypto; pub mod data_availability; pub mod deprecated_contract_class; -pub mod external_transaction; pub mod hash; pub mod internal_transaction; +pub mod rpc_transaction; pub mod serde_utils; pub mod state; pub mod transaction; diff --git a/src/rpc_transaction.rs b/src/rpc_transaction.rs new file mode 100644 index 00000000..3555613d --- /dev/null +++ b/src/rpc_transaction.rs @@ -0,0 +1,180 @@ +#[cfg(test)] +#[path = "rpc_transaction_test.rs"] +mod rpc_transaction_test; + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; +use starknet_types_core::felt::Felt; + +use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; +use crate::data_availability::DataAvailabilityMode; +use crate::state::EntryPoint; +use crate::transaction::{ + AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, Resource, ResourceBounds, + Tip, TransactionSignature, +}; + +/// Transactions that are ready to be broadcasted to the network through RPC and are not included in +/// a block. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(tag = "type")] +#[serde(deny_unknown_fields)] +pub enum RPCTransaction { + #[serde(rename = "DECLARE")] + Declare(RPCDeclareTransaction), + #[serde(rename = "DEPLOY_ACCOUNT")] + DeployAccount(RPCDeployAccountTransaction), + #[serde(rename = "INVOKE")] + Invoke(RPCInvokeTransaction), +} + +macro_rules! implement_ref_getters { + ($(($member_name:ident, $member_type:ty)), *) => { + $(pub fn $member_name(&self) -> &$member_type { + match self { + RPCTransaction::Declare( + RPCDeclareTransaction::V3(tx) + ) => &tx.$member_name, + RPCTransaction::DeployAccount( + RPCDeployAccountTransaction::V3(tx) + ) => &tx.$member_name, + RPCTransaction::Invoke( + RPCInvokeTransaction::V3(tx) + ) => &tx.$member_name + } + })* + }; +} + +impl RPCTransaction { + implement_ref_getters!( + (nonce, Nonce), + (resource_bounds, ResourceBoundsMapping), + (signature, TransactionSignature), + (tip, Tip) + ); +} + +/// A RPC declare transaction. +/// +/// This transaction is equivalent to the component DECLARE_TXN in the +/// [`Starknet specs`] with a contract class (DECLARE_TXN allows having +/// either a contract class or a class hash). +/// +/// [`Starknet specs`]: https://github.com/starkware-libs/starknet-specs/blob/master/api/starknet_api_openrpc.json +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(tag = "version")] +pub enum RPCDeclareTransaction { + #[serde(rename = "0x3")] + V3(RPCDeclareTransactionV3), +} + +/// A RPC deploy account transaction. +/// +/// This transaction is equivalent to the component DEPLOY_ACCOUNT_TXN in the +/// [`Starknet specs`]. +/// +/// [`Starknet specs`]: https://github.com/starkware-libs/starknet-specs/blob/master/api/starknet_api_openrpc.json +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(tag = "version")] +pub enum RPCDeployAccountTransaction { + #[serde(rename = "0x3")] + V3(RPCDeployAccountTransactionV3), +} + +/// A RPC invoke transaction. +/// +/// This transaction is equivalent to the component INVOKE_TXN in the +/// [`Starknet specs`]. +/// +/// [`Starknet specs`]: https://github.com/starkware-libs/starknet-specs/blob/master/api/starknet_api_openrpc.json +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(tag = "version")] +pub enum RPCInvokeTransaction { + #[serde(rename = "0x3")] + V3(RPCInvokeTransactionV3), +} + +/// A declare transaction of a Cairo-v1 contract class that can be added to Starknet through the +/// RPC. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct RPCDeclareTransactionV3 { + // TODO: Check with Shahak why we need to keep the DeclareType. + // pub r#type: DeclareType, + pub sender_address: ContractAddress, + pub compiled_class_hash: CompiledClassHash, + pub signature: TransactionSignature, + pub nonce: Nonce, + pub contract_class: ContractClass, + pub resource_bounds: ResourceBoundsMapping, + pub tip: Tip, + pub paymaster_data: PaymasterData, + pub account_deployment_data: AccountDeploymentData, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, +} + +/// A deploy account transaction that can be added to Starknet through the RPC. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct RPCDeployAccountTransactionV3 { + pub signature: TransactionSignature, + pub nonce: Nonce, + pub class_hash: ClassHash, + pub contract_address_salt: ContractAddressSalt, + pub constructor_calldata: Calldata, + pub resource_bounds: ResourceBoundsMapping, + pub tip: Tip, + pub paymaster_data: PaymasterData, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, +} + +/// An invoke account transaction that can be added to Starknet through the RPC. +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct RPCInvokeTransactionV3 { + pub sender_address: ContractAddress, + pub calldata: Calldata, + pub signature: TransactionSignature, + pub nonce: Nonce, + pub resource_bounds: ResourceBoundsMapping, + pub tip: Tip, + pub paymaster_data: PaymasterData, + pub account_deployment_data: AccountDeploymentData, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, +} + +// The contract class in SN_API state doesn't have `contract_class_version`, not following the spec. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct ContractClass { + pub sierra_program: Vec, + pub contract_class_version: String, + pub entry_points_by_type: EntryPointByType, + pub abi: String, +} + +#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct EntryPointByType { + #[serde(rename = "CONSTRUCTOR")] + pub constructor: Vec, + #[serde(rename = "EXTERNAL")] + pub external: Vec, + #[serde(rename = "L1_HANDLER")] + pub l1handler: Vec, +} + +// The serialization of the struct in transaction is in capital letters, not following the spec. +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct ResourceBoundsMapping { + pub l1_gas: ResourceBounds, + pub l2_gas: ResourceBounds, +} + +impl From for crate::transaction::ResourceBoundsMapping { + fn from(mapping: ResourceBoundsMapping) -> crate::transaction::ResourceBoundsMapping { + let map = + BTreeMap::from([(Resource::L1Gas, mapping.l1_gas), (Resource::L2Gas, mapping.l2_gas)]); + crate::transaction::ResourceBoundsMapping(map) + } +} diff --git a/src/external_transaction_test.rs b/src/rpc_transaction_test.rs similarity index 55% rename from src/external_transaction_test.rs rename to src/rpc_transaction_test.rs index 8a68c131..1eac74e3 100644 --- a/src/external_transaction_test.rs +++ b/src/rpc_transaction_test.rs @@ -1,32 +1,31 @@ -use std::collections::BTreeMap; use std::sync::Arc; use rstest::rstest; use starknet_types_core::felt::Felt; use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, PatriciaKey}; -use crate::external_transaction::{ - ContractClass, DataAvailabilityMode, ExternalDeclareTransaction, ExternalDeclareTransactionV3, - ExternalDeployAccountTransaction, ExternalDeployAccountTransactionV3, - ExternalInvokeTransaction, ExternalInvokeTransactionV3, ExternalTransaction, +use crate::rpc_transaction::{ + ContractClass, DataAvailabilityMode, RPCDeclareTransaction, RPCDeclareTransactionV3, + RPCDeployAccountTransaction, RPCDeployAccountTransactionV3, RPCInvokeTransaction, + RPCInvokeTransactionV3, RPCTransaction, ResourceBoundsMapping, }; use crate::transaction::{ - AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, Resource, ResourceBounds, - ResourceBoundsMapping, Tip, TransactionSignature, + AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBounds, Tip, + TransactionSignature, }; use crate::{contract_address, felt, patricia_key}; -fn create_resource_bounds() -> ResourceBoundsMapping { - let mut map = BTreeMap::new(); - map.insert(Resource::L1Gas, ResourceBounds { max_amount: 100, max_price_per_unit: 12 }); - map.insert(Resource::L2Gas, ResourceBounds { max_amount: 58, max_price_per_unit: 31 }); - ResourceBoundsMapping(map) +fn create_resource_bounds_for_testing() -> ResourceBoundsMapping { + ResourceBoundsMapping { + l1_gas: ResourceBounds { max_amount: 100, max_price_per_unit: 12 }, + l2_gas: ResourceBounds { max_amount: 58, max_price_per_unit: 31 }, + } } -fn create_declare_v3() -> ExternalDeclareTransaction { - ExternalDeclareTransaction::V3(ExternalDeclareTransactionV3 { +fn create_declare_v3() -> RPCDeclareTransaction { + RPCDeclareTransaction::V3(RPCDeclareTransactionV3 { contract_class: ContractClass::default(), - resource_bounds: create_resource_bounds(), + resource_bounds: create_resource_bounds_for_testing(), tip: Tip(1), signature: TransactionSignature(vec![Felt::ONE, Felt::TWO]), nonce: Nonce(Felt::ONE), @@ -39,9 +38,9 @@ fn create_declare_v3() -> ExternalDeclareTransaction { }) } -fn create_deploy_account_v3() -> ExternalDeployAccountTransaction { - ExternalDeployAccountTransaction::V3(ExternalDeployAccountTransactionV3 { - resource_bounds: create_resource_bounds(), +fn create_deploy_account_v3() -> RPCDeployAccountTransaction { + RPCDeployAccountTransaction::V3(RPCDeployAccountTransactionV3 { + resource_bounds: create_resource_bounds_for_testing(), tip: Tip::default(), contract_address_salt: ContractAddressSalt(felt!("0x23")), class_hash: ClassHash(Felt::TWO), @@ -54,9 +53,9 @@ fn create_deploy_account_v3() -> ExternalDeployAccountTransaction { }) } -fn create_invoke_v3() -> ExternalInvokeTransaction { - ExternalInvokeTransaction::V3(ExternalInvokeTransactionV3 { - resource_bounds: create_resource_bounds(), +fn create_invoke_v3() -> RPCInvokeTransaction { + RPCInvokeTransaction::V3(RPCInvokeTransactionV3 { + resource_bounds: create_resource_bounds_for_testing(), tip: Tip(50), calldata: Calldata(Arc::new(vec![felt!("0x2000"), felt!("0x1000")])), sender_address: contract_address!("0x53"), @@ -69,12 +68,13 @@ fn create_invoke_v3() -> ExternalInvokeTransaction { }) } +// We are testing the `RPCTransaction` serialization. Passing non-default values. #[rstest] -#[case(ExternalTransaction::Declare(create_declare_v3()))] -#[case(ExternalTransaction::DeployAccount(create_deploy_account_v3()))] -#[case(ExternalTransaction::Invoke(create_invoke_v3()))] -fn test_external_transactions(#[case] tx: ExternalTransaction) { +#[case(RPCTransaction::Declare(create_declare_v3()))] +#[case(RPCTransaction::DeployAccount(create_deploy_account_v3()))] +#[case(RPCTransaction::Invoke(create_invoke_v3()))] +fn test_rpc_transactions(#[case] tx: RPCTransaction) { let serialized = serde_json::to_string(&tx).unwrap(); - let deserialized: ExternalTransaction = serde_json::from_str(&serialized).unwrap(); + let deserialized: RPCTransaction = serde_json::from_str(&serialized).unwrap(); assert_eq!(tx, deserialized); }