-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathdeprecated_contract_class.rs
More file actions
234 lines (208 loc) · 7.85 KB
/
deprecated_contract_class.rs
File metadata and controls
234 lines (208 loc) · 7.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use std::collections::HashMap;
use std::num::ParseIntError;
use cairo_lang_starknet_classes::casm_contract_class::CasmContractEntryPoint;
use itertools::Itertools;
use serde::de::Error as DeserializationError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use crate::contract_class::EntryPointType;
use crate::core::EntryPointSelector;
use crate::hash::StarkHash;
use crate::serde_utils::deserialize_optional_contract_class_abi_entry_vector;
use crate::StarknetApiError;
/// A deprecated contract class.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct ContractClass {
// Starknet does not verify the abi. If we can't parse it, we set it to None.
#[serde(default, deserialize_with = "deserialize_optional_contract_class_abi_entry_vector")]
pub abi: Option<Vec<ContractClassAbiEntry>>,
pub program: Program,
/// The selector of each entry point is a unique identifier in the program.
// TODO(Yair): Consider changing to IndexMap, since this is used for computing the
// class hash.
pub entry_points_by_type: HashMap<EntryPointType, Vec<EntryPointV0>>,
}
impl ContractClass {
pub fn bytecode_length(&self) -> usize {
self.program.data.as_array().expect("The program data must be an array.").len()
}
}
/// A [ContractClass](`crate::deprecated_contract_class::ContractClass`) abi entry.
// Using untagged so the serialization will be sorted by the keys (the default behavior of Serde for
// untagged enums). We care about the order of the fields in the serialization because it affects
// the class hash calculation.
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields, untagged)]
pub enum ContractClassAbiEntry {
Constructor(FunctionAbiEntry<ConstructorType>),
Event(EventAbiEntry),
Function(FunctionAbiEntry<FunctionType>),
L1Handler(FunctionAbiEntry<L1HandlerType>),
Struct(StructAbiEntry),
}
/// An event abi entry.
// The members of the struct are sorted lexicographically for correct hash computation.
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct EventAbiEntry {
pub data: Vec<TypedParameter>,
pub keys: Vec<TypedParameter>,
pub name: String,
pub r#type: EventType,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EventType {
#[default]
Event,
}
/// A function abi entry.
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct FunctionAbiEntry<TYPE> {
pub inputs: Vec<TypedParameter>,
pub name: String,
pub outputs: Vec<TypedParameter>,
#[serde(rename = "stateMutability", default, skip_serializing_if = "Option::is_none")]
pub state_mutability: Option<FunctionStateMutability>,
pub r#type: TYPE,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FunctionType {
#[default]
Function,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ConstructorType {
#[default]
Constructor,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum L1HandlerType {
#[default]
L1Handler,
}
/// A function state mutability.
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Deserialize, Serialize)]
pub enum FunctionStateMutability {
#[serde(rename = "view")]
#[default]
View,
}
/// A struct abi entry.
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct StructAbiEntry {
pub members: Vec<StructMember>,
pub name: String,
pub size: usize,
pub r#type: StructType,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StructType {
#[default]
Struct,
}
/// A struct member for [StructAbiEntry](`crate::deprecated_contract_class::StructAbiEntry`).
// The members of the struct are sorted lexicographically for correct hash computation.
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct StructMember {
pub name: String,
pub offset: usize,
pub r#type: String,
}
/// A program corresponding to a [ContractClass](`crate::deprecated_contract_class::ContractClass`).
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct Program {
#[serde(default)]
pub attributes: serde_json::Value,
pub builtins: serde_json::Value,
#[serde(default)]
pub compiler_version: serde_json::Value,
pub data: serde_json::Value,
#[serde(default)]
pub debug_info: serde_json::Value,
#[serde(serialize_with = "serialize_hints_sorted")]
pub hints: serde_json::Value,
pub identifiers: serde_json::Value,
pub main_scope: serde_json::Value,
pub prime: serde_json::Value,
pub reference_manager: serde_json::Value,
}
// Serialize hints as a sorted mapping for correct hash computation.
fn serialize_hints_sorted<S>(hints: &serde_json::Value, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if hints.is_null() {
return serializer.serialize_none();
}
let hints_map =
hints.as_object().ok_or(serde::ser::Error::custom("Hints are not a mapping."))?;
serializer.collect_map(
hints_map
.iter()
// Parse the keys as integers and sort them.
.map(|(k, v)| Ok((k.parse::<u32>()?, v)))
.collect::<Result<Vec<_>, ParseIntError>>()
.map_err(serde::ser::Error::custom)?
.iter()
.sorted_by_key(|(k, _v)| *k)
// Convert the keys back to strings.
.map(|(k, v)| (k.to_string(), v)),
)
}
/// An entry point of a [ContractClass](`crate::deprecated_contract_class::ContractClass`).
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
pub struct EntryPointV0 {
pub selector: EntryPointSelector,
pub offset: EntryPointOffset,
}
impl TryFrom<CasmContractEntryPoint> for EntryPointV0 {
type Error = StarknetApiError;
fn try_from(value: CasmContractEntryPoint) -> Result<Self, Self::Error> {
Ok(EntryPointV0 {
selector: EntryPointSelector(StarkHash::from(value.selector)),
offset: EntryPointOffset(value.offset),
})
}
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct TypedParameter {
pub name: String,
pub r#type: String,
}
/// The offset of an [EntryPoint](`crate::state::EntryPoint`).
#[derive(
Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord,
)]
pub struct EntryPointOffset(
#[serde(deserialize_with = "number_or_string", serialize_with = "usize_to_hex")] pub usize,
);
impl TryFrom<String> for EntryPointOffset {
type Error = StarknetApiError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Ok(Self(hex_string_try_into_usize(&value)?))
}
}
pub fn number_or_string<'de, D: Deserializer<'de>>(deserializer: D) -> Result<usize, D::Error> {
let usize_value = match Value::deserialize(deserializer)? {
Value::Number(number) => number
.as_u64()
.and_then(|num_u64| usize::try_from(num_u64).ok())
.ok_or(DeserializationError::custom("Cannot cast number to usize."))?,
Value::String(s) => hex_string_try_into_usize(&s).map_err(DeserializationError::custom)?,
_ => return Err(DeserializationError::custom("Cannot cast value into usize.")),
};
Ok(usize_value)
}
fn hex_string_try_into_usize(hex_string: &str) -> Result<usize, std::num::ParseIntError> {
usize::from_str_radix(hex_string.trim_start_matches("0x"), 16)
}
fn usize_to_hex<S>(value: &usize, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(format!("{value:#x}").as_str())
}