Skip to content

Commit

Permalink
feat(transaction): implement rpc transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
MohammadNassar1 committed Apr 24, 2024
1 parent 1b46b42 commit 85ce3e0
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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;
Expand Down
144 changes: 144 additions & 0 deletions src/rpc_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#[cfg(test)]
#[path = "rpc_transaction_test.rs"]
mod rpc_transaction_test;

use serde::{Deserialize, Serialize};

use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
use crate::data_availability::DataAvailabilityMode;
use crate::hash::StarkFelt;
use crate::state::EntryPoint;
use crate::transaction::{
AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, 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")]
pub enum RPCTransaction {
#[serde(rename = "DECLARE")]
Declare(RPCDeclareTransaction),
#[serde(rename = "DEPLOY_ACCOUNT")]
DeployAccount(RPCDeployAccountTransaction),
#[serde(rename = "INVOKE")]
Invoke(RPCInvokeTransaction),
}

/// 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(deny_unknown_fields)]
#[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(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
#[serde(deny_unknown_fields)]
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<StarkFelt>,
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<EntryPoint>,
#[serde(rename = "EXTERNAL")]
pub external: Vec<EntryPoint>,
#[serde(rename = "L1_HANDLER")]
pub l1handler: Vec<EntryPoint>,
}

// The serialization of the struct in transaction is in capital letters, not following the spec.
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
pub struct ResourceBoundsMapping {
pub l1_gas: ResourceBounds,
pub l2_gas: ResourceBounds,
}
71 changes: 71 additions & 0 deletions src/rpc_transaction_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::sync::Arc;

use rstest::rstest;

use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, PatriciaKey};
use crate::hash::{StarkFelt, StarkHash};
use crate::rpc_transaction::{
ContractClass, DataAvailabilityMode, RPCDeclareTransaction, RPCDeclareTransactionV3,
RPCDeployAccountTransaction, RPCDeployAccountTransactionV3, RPCInvokeTransaction,
RPCInvokeTransactionV3, RPCTransaction, ResourceBoundsMapping,
};
use crate::transaction::{
AccountDeploymentData, Calldata, ContractAddressSalt, PaymasterData, Tip, TransactionSignature,
};
use crate::{contract_address, patricia_key, stark_felt};

fn create_declare_v3() -> RPCDeclareTransaction {
RPCDeclareTransaction::V3(RPCDeclareTransactionV3 {
contract_class: ContractClass::default(),
resource_bounds: ResourceBoundsMapping::default(),
tip: Tip(1),
signature: TransactionSignature(vec![StarkFelt::ONE, StarkFelt::TWO]),
nonce: Nonce(stark_felt!("0x1")),
compiled_class_hash: CompiledClassHash(stark_felt!("0x2")),
sender_address: contract_address!("0x3"),
nonce_data_availability_mode: DataAvailabilityMode::L1,
fee_data_availability_mode: DataAvailabilityMode::L2,
paymaster_data: PaymasterData(vec![StarkFelt::ZERO]),
account_deployment_data: AccountDeploymentData(vec![StarkFelt::THREE]),
})
}

fn create_deploy_account_v3() -> RPCDeployAccountTransaction {
RPCDeployAccountTransaction::V3(RPCDeployAccountTransactionV3 {
resource_bounds: ResourceBoundsMapping::default(),
tip: Tip::default(),
contract_address_salt: ContractAddressSalt(stark_felt!("0x23")),
class_hash: ClassHash(stark_felt!("0x2")),
constructor_calldata: Calldata(Arc::new(vec![StarkFelt::ZERO])),
nonce: Nonce(stark_felt!("0x60")),
signature: TransactionSignature(vec![StarkFelt::TWO]),
nonce_data_availability_mode: DataAvailabilityMode::L2,
fee_data_availability_mode: DataAvailabilityMode::L1,
paymaster_data: PaymasterData(vec![StarkFelt::TWO, StarkFelt::ZERO]),
})
}

fn create_invoke_v3() -> RPCInvokeTransaction {
RPCInvokeTransaction::V3(RPCInvokeTransactionV3 {
resource_bounds: ResourceBoundsMapping::default(),
tip: Tip(50),
calldata: Calldata(Arc::new(vec![stark_felt!("0x2000"), stark_felt!("0x1000")])),
sender_address: contract_address!("0x53"),
nonce: Nonce(stark_felt!("0x32")),
signature: TransactionSignature::default(),
nonce_data_availability_mode: DataAvailabilityMode::L1,
fee_data_availability_mode: DataAvailabilityMode::L1,
paymaster_data: PaymasterData(vec![StarkFelt::TWO, StarkFelt::ZERO]),
account_deployment_data: AccountDeploymentData(vec![stark_felt!("0x87")]),
})
}

#[rstest]
#[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: RPCTransaction = serde_json::from_str(&serialized).unwrap();
assert_eq!(tx, deserialized);
}

0 comments on commit 85ce3e0

Please sign in to comment.