Skip to content

Commit b6cf1bd

Browse files
committed
CairoType refactor & derive proc-macro
1 parent b343f7a commit b6cf1bd

File tree

45 files changed

+1503
-9344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1503
-9344
lines changed

Cargo.lock

Lines changed: 61 additions & 62 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
resolver = "2"
33

44
members = [
5+
"crates/cairo_type_derive",
56
"crates/dry_hint_processor",
67
"crates/dry_run",
78
"crates/fetcher",
@@ -23,7 +24,6 @@ axum = { version = "0.8", features = ["tracing"] }
2324
bincode = { version = "2.0.0-rc.3", default-features = false, features = ["serde"]}
2425
cairo-lang-casm = { version = "2.10.0-rc.1", default-features = false }
2526
cairo-lang-starknet-classes = "2.10.0-rc.1"
26-
cairo-type-derive = { git = "https://github.com/keep-starknet-strange/snos.git", rev = "35a300a10d2107482ada440b5025ee2f651afbd4" }
2727
cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "62804bcbf58b436a8986e7da0ee80333400b1ffb", features = ["extensive_hints", "clap", "cairo-1-hints"] }
2828
clap = { version = "4.3.10", features = ["derive"] }
2929
eth-trie-proofs = { git = "https://github.com/HerodotusDev/eth-trie-proofs.git" }
@@ -57,6 +57,7 @@ utoipa = { version = "5.3.1", features = ["axum_extras"] }
5757
utoipa-swagger-ui = { version = "9", features = ["axum"] }
5858
version-compare = "=0.0.11"
5959

60+
cairo_type_derive = { path = "crates/cairo_type_derive" }
6061
dry_hint_processor = { path = "crates/dry_hint_processor" }
6162
eth_essentials_cairo_vm_hints = { path = "packages/eth_essentials/cairo_vm_hints" }
6263
fetcher = { path = "crates/fetcher" }
@@ -65,4 +66,4 @@ indexer = { path = "crates/indexer" }
6566
pathfinder_gateway_types = { git = "https://github.com/eqlabs/pathfinder", package = "starknet-gateway-types" }
6667
sound_hint_processor = { path = "crates/sound_hint_processor" }
6768
syscall_handler = { path = "crates/syscall_handler" }
68-
types = { path = "crates/types" }
69+
types = { path = "crates/types" }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "cairo_type_derive"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
quote = "1.0.35"
11+
syn = "2.0.48"
12+
proc-macro2 = "1.0.78"
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
extern crate proc_macro;
2+
3+
use quote::{format_ident, quote};
4+
use syn::{parse_macro_input, Data, DeriveInput, Fields, Type};
5+
6+
#[proc_macro_derive(CairoType)]
7+
pub fn cairo_type_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
8+
// Parse the input tokens into a syntax tree
9+
let input = parse_macro_input!(input as DeriveInput);
10+
11+
// Get the identifier of the struct
12+
let struct_ident = &input.ident;
13+
14+
// Generate code to implement the trait
15+
let expanded = match &input.data {
16+
Data::Struct(data_struct) => match &data_struct.fields {
17+
Fields::Named(fields) => {
18+
let field_names_read = fields.named.iter().map(|f| &f.ident);
19+
let field_names_write = field_names_read.clone();
20+
let n_fields = field_names_read.clone().count();
21+
let field_values = field_names_read.clone().enumerate().map(|(index, field_name)| {
22+
quote! {
23+
let #field_name = vm.get_integer((address + #index)?)?.into_owned();
24+
}
25+
});
26+
27+
quote! {
28+
impl CairoType for #struct_ident {
29+
fn from_memory(vm: &VirtualMachine, address: Relocatable) -> Result<Self, MemoryError> {
30+
#(#field_values)*
31+
Ok(Self {
32+
#( #field_names_read ),*
33+
})
34+
}
35+
fn to_memory(&self, vm: &mut VirtualMachine, address: Relocatable) -> Result<Relocatable, MemoryError> {
36+
let mut offset = 0;
37+
#(vm.insert_value((address + offset)?, &self.#field_names_write)?; offset += 1;)*
38+
39+
Ok((address + offset)?)
40+
}
41+
42+
fn n_fields(vm: &VirtualMachine, address: Relocatable) -> Result<usize, MemoryError> {
43+
Ok(#n_fields)
44+
}
45+
}
46+
}
47+
}
48+
Fields::Unnamed(_) | Fields::Unit => {
49+
// Unsupported field types
50+
quote! {
51+
compile_error!("CairoType only supports structs with named fields");
52+
}
53+
}
54+
},
55+
Data::Enum(_) | Data::Union(_) => {
56+
// Unsupported data types
57+
quote! {
58+
compile_error!("CairoType only supports structs");
59+
}
60+
}
61+
};
62+
63+
// Convert the generated code into a TokenStream and return it
64+
proc_macro::TokenStream::from(expanded)
65+
}
66+
67+
fn field_size(field: &syn::Field) -> proc_macro2::TokenStream {
68+
if let Type::Path(type_path) = &field.ty {
69+
if let Some(segment) = type_path.path.segments.last() {
70+
let type_name = &segment.ident;
71+
if type_name == "Felt252" || type_name == "Relocatable" {
72+
quote! { 1 }
73+
} else {
74+
quote! { #type_name::cairo_size() }
75+
}
76+
} else {
77+
let field_name = field.ident.as_ref().unwrap();
78+
quote! {
79+
compile_error!("Could not determine the size of {}.", #field_name);
80+
}
81+
}
82+
} else {
83+
quote! {
84+
compile_error!("Could not determine the size of all fields in the struct. This derive macro is only compatible with Felt252 fields.");
85+
}
86+
}
87+
}
88+
89+
/// Provides a method to compute the address of each field
90+
#[proc_macro_derive(FieldOffsetGetters)]
91+
pub fn get_field_offsets_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
92+
// Parse the input tokens into a syntax tree
93+
let input = parse_macro_input!(input as DeriveInput);
94+
95+
// Get the identifier of the struct
96+
let struct_ident = &input.ident;
97+
98+
// Generate code to implement the trait
99+
let getters = match &input.data {
100+
Data::Struct(data_struct) => {
101+
// Extract fields' names and types
102+
let fields = match &data_struct.fields {
103+
Fields::Named(fields) => &fields.named,
104+
_ => {
105+
return quote! {
106+
compile_error!("FieldOffsetGetters only supports structs with named fields");
107+
}
108+
.into();
109+
}
110+
};
111+
112+
let mut field_sizes: Vec<proc_macro2::TokenStream> = vec![];
113+
let mut get_field_offset_methods: Vec<proc_macro2::TokenStream> = vec![];
114+
115+
for field in fields {
116+
let field_name = field.ident.as_ref().expect("Expected named field");
117+
let offset_fn_name = format_ident!("{}_offset", field_name);
118+
119+
let rendered_offset_impl = if field_sizes.is_empty() {
120+
quote! { 0 }
121+
} else {
122+
quote! { #(#field_sizes)+* }
123+
};
124+
125+
let get_offset_method = quote! {
126+
pub fn #offset_fn_name() -> usize {
127+
#rendered_offset_impl
128+
}
129+
};
130+
get_field_offset_methods.push(get_offset_method);
131+
field_sizes.push(field_size(field));
132+
}
133+
134+
// Combine all setter methods
135+
quote! {
136+
impl #struct_ident {
137+
#( #get_field_offset_methods )*
138+
pub fn cairo_size() -> usize {
139+
#(#field_sizes)+*
140+
}
141+
}
142+
}
143+
}
144+
_ => {
145+
quote! {
146+
compile_error!("FieldOffsetGetters only supports structs");
147+
}
148+
}
149+
};
150+
151+
// Convert the generated code into a TokenStream and return it
152+
getters.into()
153+
}

crates/dry_hint_processor/src/syscall_handler/evm/account.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl CallHandler for AccountCallHandler {
2323

2424
fn derive_key(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Key> {
2525
let ret = CairoKey::from_memory(vm, *ptr)?;
26-
*ptr = (*ptr + CairoKey::n_fields())?;
26+
*ptr = (*ptr + CairoKey::n_fields(vm, *ptr)?)?;
2727
ret.try_into()
2828
.map_err(|e| SyscallExecutionError::InternalError(format!("{}", e).into()))
2929
}

crates/dry_hint_processor/src/syscall_handler/evm/header.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl CallHandler for HeaderCallHandler {
2828

2929
fn derive_key(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Key> {
3030
let ret = CairoKey::from_memory(vm, *ptr)?;
31-
*ptr = (*ptr + CairoKey::n_fields())?;
31+
*ptr = (*ptr + CairoKey::n_fields(vm, *ptr)?)?;
3232
ret.try_into()
3333
.map_err(|e| SyscallExecutionError::InternalError(format!("{}", e).into()))
3434
}

crates/dry_hint_processor/src/syscall_handler/evm/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,39 +70,39 @@ impl SyscallHandler for CallContractHandler {
7070
let result = header::HeaderCallHandler.handle(key.clone(), function_id, vm).await?;
7171
self.key_set.insert(DryRunKey::Header(key));
7272
result.to_memory(vm, retdata_end)?;
73-
retdata_end += <header::HeaderCallHandler as CallHandler>::CallHandlerResult::n_fields();
73+
retdata_end += <header::HeaderCallHandler as CallHandler>::CallHandlerResult::n_fields(vm, retdata_end)?;
7474
}
7575
CallHandlerId::Account => {
7676
let key = account::AccountCallHandler::derive_key(vm, &mut calldata)?;
7777
let function_id = account::AccountCallHandler::derive_id(request.selector)?;
7878
let result = account::AccountCallHandler.handle(key.clone(), function_id, vm).await?;
7979
self.key_set.insert(DryRunKey::Account(key));
8080
result.to_memory(vm, retdata_end)?;
81-
retdata_end += <account::AccountCallHandler as CallHandler>::CallHandlerResult::n_fields();
81+
retdata_end += <account::AccountCallHandler as CallHandler>::CallHandlerResult::n_fields(vm, retdata_end)?;
8282
}
8383
CallHandlerId::Storage => {
8484
let key = storage::StorageCallHandler::derive_key(vm, &mut calldata)?;
8585
let function_id = storage::StorageCallHandler::derive_id(request.selector)?;
8686
let result = storage::StorageCallHandler.handle(key.clone(), function_id, vm).await?;
8787
self.key_set.insert(DryRunKey::Storage(key));
8888
result.to_memory(vm, retdata_end)?;
89-
retdata_end += <storage::StorageCallHandler as CallHandler>::CallHandlerResult::n_fields();
89+
retdata_end += <storage::StorageCallHandler as CallHandler>::CallHandlerResult::n_fields(vm, retdata_end)?;
9090
}
9191
CallHandlerId::Transaction => {
9292
let key = transaction::TransactionCallHandler::derive_key(vm, &mut calldata)?;
9393
let function_id = transaction::TransactionCallHandler::derive_id(request.selector)?;
9494
let result = transaction::TransactionCallHandler.handle(key.clone(), function_id, vm).await?;
9595
self.key_set.insert(DryRunKey::Tx(key));
9696
result.to_memory(vm, retdata_end)?;
97-
retdata_end += <transaction::TransactionCallHandler as CallHandler>::CallHandlerResult::n_fields();
97+
retdata_end += <transaction::TransactionCallHandler as CallHandler>::CallHandlerResult::n_fields(vm, retdata_end)?;
9898
}
9999
CallHandlerId::Receipt => {
100100
let key = receipt::ReceiptCallHandler::derive_key(vm, &mut calldata)?;
101101
let function_id = receipt::ReceiptCallHandler::derive_id(request.selector)?;
102102
let result = receipt::ReceiptCallHandler.handle(key.clone(), function_id, vm).await?;
103103
self.key_set.insert(DryRunKey::Receipt(key));
104104
result.to_memory(vm, retdata_end)?;
105-
retdata_end += <receipt::ReceiptCallHandler as CallHandler>::CallHandlerResult::n_fields();
105+
retdata_end += <receipt::ReceiptCallHandler as CallHandler>::CallHandlerResult::n_fields(vm, retdata_end)?;
106106
}
107107
}
108108

crates/dry_hint_processor/src/syscall_handler/evm/receipt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl CallHandler for ReceiptCallHandler {
2727

2828
fn derive_key(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Key> {
2929
let ret = CairoKey::from_memory(vm, *ptr)?;
30-
*ptr = (*ptr + CairoKey::n_fields())?;
30+
*ptr = (*ptr + CairoKey::n_fields(vm, *ptr)?)?;
3131
ret.try_into()
3232
.map_err(|e| SyscallExecutionError::InternalError(format!("{}", e).into()))
3333
}

crates/dry_hint_processor/src/syscall_handler/evm/storage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl CallHandler for StorageCallHandler {
2323

2424
fn derive_key(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Key> {
2525
let ret = CairoKey::from_memory(vm, *ptr)?;
26-
*ptr = (*ptr + CairoKey::n_fields())?;
26+
*ptr = (*ptr + CairoKey::n_fields(vm, *ptr)?)?;
2727
ret.try_into()
2828
.map_err(|e| SyscallExecutionError::InternalError(format!("{}", e).into()))
2929
}

crates/dry_hint_processor/src/syscall_handler/evm/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl CallHandler for TransactionCallHandler {
2828

2929
fn derive_key(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Key> {
3030
let ret = CairoKey::from_memory(vm, *ptr)?;
31-
*ptr = (*ptr + CairoKey::n_fields())?;
31+
*ptr = (*ptr + CairoKey::n_fields(vm, *ptr)?)?;
3232
ret.try_into()
3333
.map_err(|e| SyscallExecutionError::InternalError(format!("{}", e).into()))
3434
}

0 commit comments

Comments
 (0)