From 8e7cf729d56ce5a72e940529997955211079e695 Mon Sep 17 00:00:00 2001 From: mohammad-starkware <130282237+MohammadNassar1@users.noreply.github.com> Date: Wed, 29 May 2024 17:07:18 +0300 Subject: [PATCH] feat(transaction): implement rpc transaction (#236) --- src/external_transaction.rs | 131 +++++++++++++++++-------------- src/external_transaction_test.rs | 23 +++--- 2 files changed, 86 insertions(+), 68 deletions(-) diff --git a/src/external_transaction.rs b/src/external_transaction.rs index 23012092..6bf44844 100644 --- a/src/external_transaction.rs +++ b/src/external_transaction.rs @@ -2,30 +2,28 @@ #[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::hash::StarkFelt; +use crate::state::EntryPoint; use crate::transaction::{ - AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBoundsMapping, - Tip, TransactionSignature, + AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBounds, Tip, + TransactionSignature, }; -/// An external transaction. +/// 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 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")] + #[serde(rename = "INVOKE")] Invoke(ExternalInvokeTransaction), } @@ -54,9 +52,13 @@ impl ExternalTransaction { ); } -/// 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. +/// 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 ExternalDeclareTransaction { @@ -64,20 +66,26 @@ pub enum ExternalDeclareTransaction { 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)] +/// 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 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)] +/// 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 ExternalInvokeTransaction { #[serde(rename = "0x3")] @@ -85,67 +93,76 @@ pub enum ExternalInvokeTransaction { } /// 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. +/// RPC. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] pub struct ExternalDeclareTransactionV3 { + // 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 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, + pub nonce_data_availability_mode: DataAvailabilityMode, + pub fee_data_availability_mode: DataAvailabilityMode, } -/// 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)] +/// 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 ExternalDeployAccountTransactionV3 { - pub resource_bounds: ResourceBoundsMapping, - pub tip: Tip, - pub contract_address_salt: ContractAddressSalt, + pub signature: TransactionSignature, + pub nonce: Nonce, pub class_hash: ClassHash, + pub contract_address_salt: ContractAddressSalt, pub constructor_calldata: Calldata, - pub nonce: Nonce, - pub signature: TransactionSignature, + pub resource_bounds: ResourceBoundsMapping, + pub tip: Tip, + pub paymaster_data: PaymasterData, 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)] +/// 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 ExternalInvokeTransactionV3 { - pub resource_bounds: ResourceBoundsMapping, - pub tip: Tip, - pub calldata: Calldata, pub sender_address: ContractAddress, - pub nonce: Nonce, + pub calldata: Calldata, pub signature: TransactionSignature, - pub nonce_data_availability_mode: DataAvailabilityMode, - pub fee_data_availability_mode: DataAvailabilityMode, + 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 { - #[serde(rename = "sierra_program")] - pub compressed_sierra_program: String, + pub sierra_program: Vec, pub contract_class_version: String, - pub entry_points_by_type: HashMap>, + 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, +} diff --git a/src/external_transaction_test.rs b/src/external_transaction_test.rs index 1b93d17d..99a9f070 100644 --- a/src/external_transaction_test.rs +++ b/src/external_transaction_test.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use std::sync::Arc; use rstest::rstest; @@ -8,25 +7,26 @@ use crate::external_transaction::{ ContractClass, DataAvailabilityMode, ExternalDeclareTransaction, ExternalDeclareTransactionV3, ExternalDeployAccountTransaction, ExternalDeployAccountTransactionV3, ExternalInvokeTransaction, ExternalInvokeTransactionV3, ExternalTransaction, + ResourceBoundsMapping, }; use crate::hash::{StarkFelt, StarkHash}; use crate::transaction::{ - AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, Resource, ResourceBounds, - ResourceBoundsMapping, Tip, TransactionSignature, + AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, ResourceBounds, Tip, + TransactionSignature, }; use crate::{contract_address, patricia_key, stark_felt}; -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 { contract_class: ContractClass::default(), - resource_bounds: create_resource_bounds(), + resource_bounds: create_resource_bounds_for_testing(), tip: Tip(1), signature: TransactionSignature(vec![StarkFelt::ONE, StarkFelt::TWO]), nonce: Nonce(stark_felt!("0x1")), @@ -41,7 +41,7 @@ fn create_declare_v3() -> ExternalDeclareTransaction { fn create_deploy_account_v3() -> ExternalDeployAccountTransaction { ExternalDeployAccountTransaction::V3(ExternalDeployAccountTransactionV3 { - resource_bounds: create_resource_bounds(), + resource_bounds: create_resource_bounds_for_testing(), tip: Tip::default(), contract_address_salt: ContractAddressSalt(stark_felt!("0x23")), class_hash: ClassHash(stark_felt!("0x2")), @@ -56,7 +56,7 @@ fn create_deploy_account_v3() -> ExternalDeployAccountTransaction { fn create_invoke_v3() -> ExternalInvokeTransaction { ExternalInvokeTransaction::V3(ExternalInvokeTransactionV3 { - resource_bounds: create_resource_bounds(), + resource_bounds: create_resource_bounds_for_testing(), tip: Tip(50), calldata: Calldata(Arc::new(vec![stark_felt!("0x2000"), stark_felt!("0x1000")])), sender_address: contract_address!("0x53"), @@ -69,6 +69,7 @@ fn create_invoke_v3() -> ExternalInvokeTransaction { }) } +// We are testing the `ExternalTransaction` serialization. Passing non-default values. #[rstest] #[case(ExternalTransaction::Declare(create_declare_v3()))] #[case(ExternalTransaction::DeployAccount(create_deploy_account_v3()))]