Skip to content

Commit c17ced3

Browse files
committed
feat: preserve class abi
1 parent a17d6e6 commit c17ced3

File tree

21 files changed

+355
-110
lines changed

21 files changed

+355
-110
lines changed

Cargo.lock

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/executor/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ thiserror.workspace = true
1919
tracing.workspace = true
2020

2121
# cairo-native
22+
cairo-lang-starknet-classes = { workspace = true, optional = true }
2223
cairo-native = { version = "0.4.1", optional = true }
2324
cairo-vm.workspace = true
2425
parking_lot.workspace = true

crates/executor/src/implementation/blockifier/cache.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ impl ClassCache {
250250
use cairo_native::OptLevel;
251251

252252
#[cfg(feature = "native")]
253-
let program = sierra.extract_sierra_program().unwrap();
253+
let program = cairo_lang_starknet_classes::contract_class::ContractClass::from(
254+
sierra.clone(), // TODO: avoid cloning here
255+
)
256+
.extract_sierra_program()
257+
.unwrap();
258+
254259
#[cfg(feature = "native")]
255260
let entry_points = sierra.entry_points_by_type.clone();
256261

crates/gateway/gateway-types/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use katana_primitives::class::{ClassHash, CompiledClassHash};
2727
use katana_primitives::contract::{Nonce, StorageKey, StorageValue};
2828
use katana_primitives::da::L1DataAvailabilityMode;
2929
use katana_primitives::{ContractAddress, Felt};
30-
pub use katana_rpc_types::class::SierraClass;
30+
pub use katana_rpc_types::class::RpcSierraContractClass;
3131
use serde::{Deserialize, Serialize};
3232
use starknet::core::types::ResourcePrice;
3333

crates/gateway/gateway-types/src/transaction.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use katana_primitives::transaction::{
2424
TxWithHash,
2525
};
2626
use katana_primitives::{ContractAddress, Felt};
27-
use katana_rpc_types::SierraClassAbi;
2827
use serde::{Deserialize, Deserializer, Serialize};
2928

3029
/// API response for an INVOKE_FUNCTION transaction
@@ -257,13 +256,15 @@ pub struct CompressedSierraClass {
257256
pub sierra_program: CompressedSierraProgram,
258257
pub contract_class_version: String,
259258
pub entry_points_by_type: ContractEntryPoints,
260-
pub abi: SierraClassAbi,
259+
pub abi: Option<String>,
261260
}
262261

263-
impl TryFrom<katana_rpc_types::class::SierraClass> for CompressedSierraClass {
262+
impl TryFrom<katana_rpc_types::class::RpcSierraContractClass> for CompressedSierraClass {
264263
type Error = CompressedSierraProgramError;
265264

266-
fn try_from(value: katana_rpc_types::class::SierraClass) -> Result<Self, Self::Error> {
265+
fn try_from(
266+
value: katana_rpc_types::class::RpcSierraContractClass,
267+
) -> Result<Self, Self::Error> {
267268
let abi = value.abi;
268269
let entry_points_by_type = value.entry_points_by_type;
269270
let contract_class_version = value.contract_class_version;
@@ -272,7 +273,7 @@ impl TryFrom<katana_rpc_types::class::SierraClass> for CompressedSierraClass {
272273
}
273274
}
274275

275-
impl TryFrom<CompressedSierraClass> for katana_rpc_types::class::SierraClass {
276+
impl TryFrom<CompressedSierraClass> for katana_rpc_types::class::RpcSierraContractClass {
276277
type Error = CompressedSierraProgramError;
277278

278279
fn try_from(value: CompressedSierraClass) -> Result<Self, Self::Error> {
@@ -724,7 +725,7 @@ mod tests {
724725

725726
use katana_primitives::fee::{ResourceBounds, ResourceBoundsMapping, Tip};
726727
use katana_primitives::{address, Felt};
727-
use katana_rpc_types::SierraClass;
728+
use katana_rpc_types::RpcSierraContractClass;
728729

729730
use super::*;
730731

@@ -772,7 +773,7 @@ mod tests {
772773

773774
#[test]
774775
fn test_conversion_from_rpc_query_declare_tx() {
775-
let sierra_class = Arc::new(katana_rpc_types::class::SierraClass {
776+
let sierra_class = Arc::new(katana_rpc_types::class::RpcSierraContractClass {
776777
sierra_program: vec![Felt::from(0x123), Felt::from(0x456)],
777778
contract_class_version: "0.1.0".to_string(),
778779
entry_points_by_type: Default::default(),
@@ -820,7 +821,7 @@ mod tests {
820821
assert_eq!(gateway_tx.fee_data_availability_mode, rpc_tx.fee_data_availability_mode.into());
821822

822823
// convert the gateway contract class to rpc contract class and ensure they are equal
823-
let converted_sierra_class: SierraClass =
824+
let converted_sierra_class: RpcSierraContractClass =
824825
gateway_tx.contract_class.as_ref().clone().try_into().unwrap();
825826
assert_eq!(converted_sierra_class, sierra_class.as_ref().clone());
826827
}

crates/primitives/Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ version.workspace = true
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
cairo-vm.workspace = true
11+
cairo-lang-sierra.workspace = true
12+
cairo-lang-utils.workspace = true
13+
cairo-lang-starknet-classes.workspace = true
14+
1015
anyhow.workspace = true
1116
arbitrary = { workspace = true, optional = true }
1217
blockifier = { workspace = true, features = [ "testing" ] } # some Clone derives are gated behind 'testing' feature
1318
cainome-cairo-serde.workspace = true
14-
cairo-lang-starknet-classes.workspace = true
15-
cairo-vm.workspace = true
1619
derive_more.workspace = true
1720
heapless = { version = "0.8.0", features = [ "serde" ] }
1821
lazy_static.workspace = true

crates/primitives/src/class.rs

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::str::FromStr;
22

3-
use cairo_lang_starknet_classes::abi;
43
use cairo_lang_starknet_classes::casm_contract_class::StarknetSierraCompilationError;
54
use cairo_lang_starknet_classes::contract_class::{
65
version_id_from_serialized_sierra_program, ContractEntryPoint, ContractEntryPoints,
76
};
7+
use cairo_lang_utils::bigint::BigUintAsHex;
88
use serde_json_pythonic::to_string_pythonic;
99
use starknet::macros::short_string;
1010
use starknet_api::contract_class::SierraVersion;
@@ -18,8 +18,6 @@ pub type ClassHash = Felt;
1818
/// The hash of a compiled contract class.
1919
pub type CompiledClassHash = Felt;
2020

21-
/// The canonical contract class (Sierra) type.
22-
pub type SierraContractClass = cairo_lang_starknet_classes::contract_class::ContractClass;
2321
/// The canonical legacy class (Cairo 0) type.
2422
pub type LegacyContractClass = starknet_api::deprecated_contract_class::ContractClass;
2523

@@ -29,6 +27,97 @@ pub type CasmContractClass = cairo_lang_starknet_classes::casm_contract_class::C
2927
/// ABI for Sierra-based classes.
3028
pub type ContractAbi = cairo_lang_starknet_classes::abi::Contract;
3129

30+
#[derive(Debug, Clone, Eq, PartialEq)]
31+
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize), serde(untagged))]
32+
pub enum MaybeInvalidSierraContractAbi {
33+
Valid(ContractAbi),
34+
Invalid(String),
35+
}
36+
37+
impl std::fmt::Display for MaybeInvalidSierraContractAbi {
38+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39+
match self {
40+
MaybeInvalidSierraContractAbi::Valid(abi) => {
41+
let s = to_string_pythonic(abi).expect("failed to serialize abi");
42+
write!(f, "{}", s)
43+
}
44+
MaybeInvalidSierraContractAbi::Invalid(abi) => write!(f, "{}", abi),
45+
}
46+
}
47+
}
48+
49+
impl From<String> for MaybeInvalidSierraContractAbi {
50+
fn from(value: String) -> Self {
51+
match serde_json::from_str::<ContractAbi>(&value) {
52+
Ok(abi) => MaybeInvalidSierraContractAbi::Valid(abi),
53+
Err(..) => MaybeInvalidSierraContractAbi::Invalid(value),
54+
}
55+
}
56+
}
57+
58+
impl From<&str> for MaybeInvalidSierraContractAbi {
59+
fn from(value: &str) -> Self {
60+
match serde_json::from_str::<ContractAbi>(value) {
61+
Ok(abi) => MaybeInvalidSierraContractAbi::Valid(abi),
62+
Err(..) => MaybeInvalidSierraContractAbi::Invalid(value.to_string()),
63+
}
64+
}
65+
}
66+
67+
/// Represents a contract in the Starknet network.
68+
///
69+
/// The canonical contract class (Sierra) type.
70+
#[derive(Clone, Debug, PartialEq, Eq)]
71+
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
72+
pub struct SierraContractClass {
73+
pub sierra_program: Vec<BigUintAsHex>,
74+
pub sierra_program_debug_info: Option<cairo_lang_sierra::debug_info::DebugInfo>,
75+
pub contract_class_version: String,
76+
pub entry_points_by_type: ContractEntryPoints,
77+
pub abi: Option<MaybeInvalidSierraContractAbi>,
78+
}
79+
80+
impl SierraContractClass {
81+
/// Computes the hash of the Sierra contract class.
82+
pub fn hash(&self) -> ClassHash {
83+
let Self { sierra_program, abi, entry_points_by_type, .. } = self;
84+
85+
let program: Vec<Felt> = sierra_program.iter().map(|f| f.value.clone().into()).collect();
86+
let abi: String = abi.as_ref().map(|abi| abi.to_string()).unwrap_or_default();
87+
88+
compute_sierra_class_hash(&abi, entry_points_by_type, &program)
89+
}
90+
}
91+
92+
impl From<SierraContractClass> for cairo_lang_starknet_classes::contract_class::ContractClass {
93+
fn from(value: SierraContractClass) -> Self {
94+
let abi = value.abi.and_then(|abi| match abi {
95+
MaybeInvalidSierraContractAbi::Invalid(..) => None,
96+
MaybeInvalidSierraContractAbi::Valid(abi) => Some(abi),
97+
});
98+
99+
cairo_lang_starknet_classes::contract_class::ContractClass {
100+
abi,
101+
sierra_program: value.sierra_program,
102+
entry_points_by_type: value.entry_points_by_type,
103+
contract_class_version: value.contract_class_version,
104+
sierra_program_debug_info: value.sierra_program_debug_info,
105+
}
106+
}
107+
}
108+
109+
impl From<cairo_lang_starknet_classes::contract_class::ContractClass> for SierraContractClass {
110+
fn from(value: cairo_lang_starknet_classes::contract_class::ContractClass) -> Self {
111+
SierraContractClass {
112+
abi: value.abi.map(MaybeInvalidSierraContractAbi::Valid),
113+
sierra_program: value.sierra_program,
114+
entry_points_by_type: value.entry_points_by_type,
115+
contract_class_version: value.contract_class_version,
116+
sierra_program_debug_info: value.sierra_program_debug_info,
117+
}
118+
}
119+
}
120+
32121
#[derive(Debug, thiserror::Error)]
33122
pub enum ContractClassCompilationError {
34123
#[error(transparent)]
@@ -51,23 +140,7 @@ impl ContractClass {
51140
/// Computes the hash of the class.
52141
pub fn class_hash(&self) -> Result<ClassHash, ComputeClassHashError> {
53142
match self {
54-
Self::Class(class) => {
55-
// Technically we don't have to use the Pythonic JSON style here. Doing this just to
56-
// align with the official `cairo-lang` CLI.
57-
//
58-
// TODO: add an `AbiFormatter` trait and let users choose which one to use.
59-
let abi = class.abi.as_ref();
60-
let abi_str = to_string_pythonic(abi.unwrap_or(&abi::Contract::default())).unwrap();
61-
62-
let sierra_program = &class
63-
.sierra_program
64-
.iter()
65-
.map(|f| f.value.clone().into())
66-
.collect::<Vec<Felt>>();
67-
68-
Ok(compute_sierra_class_hash(&abi_str, &class.entry_points_by_type, sierra_program))
69-
}
70-
143+
Self::Class(class) => Ok(class.hash()),
71144
Self::Legacy(class) => compute_legacy_class_hash(class),
72145
}
73146
}
@@ -77,7 +150,7 @@ impl ContractClass {
77150
match self {
78151
Self::Legacy(class) => Ok(CompiledClass::Legacy(class)),
79152
Self::Class(class) => {
80-
let casm = CasmContractClass::from_contract_class(class, true, usize::MAX)?;
153+
let casm = CasmContractClass::from_contract_class(class.into(), true, usize::MAX)?;
81154
let casm = CompiledClass::Class(casm);
82155
Ok(casm)
83156
}

crates/rpc/rpc-server/tests/starknet.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ async fn get_compiled_casm() {
166166
// Setup expected compiled class data to verify against
167167

168168
use katana_primitives::class::{ContractClass, SierraContractClass};
169-
use katana_rpc_types::SierraClass as RpcSierraClass;
169+
use katana_rpc_types::RpcSierraContractClass;
170170

171-
let rpc_class = RpcSierraClass::try_from(contract).unwrap();
171+
let rpc_class = RpcSierraContractClass::try_from(contract).unwrap();
172172
let class = SierraContractClass::try_from(rpc_class).unwrap();
173173
let expected_casm = ContractClass::Class(class).compile().unwrap();
174174

crates/rpc/rpc-types/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ katana-genesis.workspace = true
1212
katana-trie.workspace = true
1313
serde-utils.workspace = true
1414

15-
derive_more.workspace = true
1615
cainome.workspace = true
1716
cainome-cairo-serde.workspace = true
1817
cairo-lang-starknet-classes.workspace = true

crates/rpc/rpc-types/src/broadcasted.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use katana_primitives::utils::get_contract_address;
1515
use katana_primitives::{ContractAddress, Felt};
1616
use serde::{de, Deserialize, Deserializer, Serialize};
1717

18-
use crate::class::SierraClass;
18+
use crate::class::RpcSierraContractClass;
1919

2020
pub const QUERY_VERSION_OFFSET: Felt =
2121
Felt::from_raw([576460752142434320, 18446744073709551584, 17407, 18446744073700081665]);
@@ -71,7 +71,7 @@ pub struct UntypedBroadcastedTx {
7171
pub compiled_class_hash: Option<CompiledClassHash>,
7272

7373
#[serde(default, skip_serializing_if = "Option::is_none")]
74-
pub contract_class: Option<Arc<SierraClass>>,
74+
pub contract_class: Option<Arc<RpcSierraContractClass>>,
7575

7676
// Invoke & Declare only field
7777
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -476,7 +476,7 @@ pub struct BroadcastedDeclareTx {
476476
/// a transaction to be valid for execution, the nonce must be equal to the account's current
477477
/// nonce.
478478
pub nonce: Nonce,
479-
pub contract_class: Arc<SierraClass>,
479+
pub contract_class: Arc<RpcSierraContractClass>,
480480
/// Data needed to allow the paymaster to pay for the transaction in native tokens.
481481
pub paymaster_data: Vec<Felt>,
482482
/// The tip for the transaction.

0 commit comments

Comments
 (0)