Skip to content

Commit

Permalink
add get_class_hash_at_syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
dean-starkware committed Sep 25, 2024
1 parent 883047e commit aee1129
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 6 deletions.
3 changes: 2 additions & 1 deletion corelib/src/starknet.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ pub mod syscalls;
use syscalls::{
call_contract_syscall, deploy_syscall, emit_event_syscall, get_block_hash_syscall,
get_execution_info_syscall, library_call_syscall, send_message_to_l1_syscall,
storage_read_syscall, storage_write_syscall, replace_class_syscall, keccak_syscall
storage_read_syscall, storage_write_syscall, replace_class_syscall, keccak_syscall,
get_class_hash_at_syscall
};

/// secp256
Expand Down
9 changes: 9 additions & 0 deletions corelib/src/starknet/syscalls.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ pub extern fn replace_class_syscall(
) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic;


/// Gets the class hash of the contract at the given address.
/// `contract_address` - The address of the deployed contract.
///
/// Returns the class hash of the contract's originating code.
pub extern fn get_class_hash_at_syscall(
contract_address: ContractAddress
) -> SyscallResult<ClassHash> implicits(GasBuiltin, System) nopanic;


/// Computes the keccak of the input.
/// The system call does not add any padding and the input needs to be a multiple of 1088 bits
/// (== 17 u64 word).
Expand Down
21 changes: 21 additions & 0 deletions crates/cairo-lang-runner/src/casm_run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ mod gas_costs {
pub const EMIT_EVENT: usize = 10 * STEP;
pub const GET_BLOCK_HASH: usize = 50 * STEP;
pub const GET_EXECUTION_INFO: usize = 10 * STEP;
pub const GET_CLASS_HASH_AT: usize = 50 * STEP;
pub const KECCAK: usize = 0;
pub const KECCAK_ROUND_COST: usize = 180000;
pub const SHA256_PROCESS_BLOCK: usize = 1852 * STEP + 65 * RANGE_CHECK + 1115 * BITWISE;
Expand Down Expand Up @@ -799,6 +800,9 @@ impl<'a> CairoHintProcessor<'a> {
"ReplaceClass" => execute_handle_helper(&mut |system_buffer, gas_counter| {
self.replace_class(gas_counter, system_buffer.next_felt252()?.into_owned())
}),
"GetClassHashAt" => execute_handle_helper(&mut |system_buffer, gas_counter| {
self.get_class_hash_at(gas_counter, system_buffer.next_felt252()?.into_owned())
}),
_ => panic!("Unknown selector for system call!"),
}
}
Expand Down Expand Up @@ -1114,6 +1118,23 @@ impl<'a> CairoHintProcessor<'a> {
Ok(SyscallResult::Success(vec![]))
}

/// Executes the `get_class_hash_at_syscall` syscall.
fn get_class_hash_at(
&mut self,
gas_counter: &mut usize,
contract_address: Felt252,
) -> Result<SyscallResult, HintError> {
deduct_gas!(gas_counter, GET_CLASS_HASH_AT);
// Look up the class hash of the deployed contract at the given address.
let class_hash = self
.starknet_state
.deployed_contracts
.get(&contract_address)
.cloned()
.unwrap_or_else(Felt252::zero);
Ok(SyscallResult::Success(vec![MaybeRelocatable::Int(class_hash)]))
}

/// Executes the entry point with the given calldata.
fn call_entry_point(
&mut self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ pub fn core_libfunc_ap_change<InfoProvider: InvocationApChangeInfoProvider>(
| StarkNetConcreteLibfunc::LibraryCall(_)
| StarkNetConcreteLibfunc::ReplaceClass(_)
| StarkNetConcreteLibfunc::SendMessageToL1(_)
| StarkNetConcreteLibfunc::Secp256(_) => {
| StarkNetConcreteLibfunc::Secp256(_)
| StarkNetConcreteLibfunc::GetClassHashAt(_) => {
vec![ApChange::Known(2), ApChange::Known(2)]
}
StarkNetConcreteLibfunc::Testing(libfunc) => match libfunc {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub fn starknet_libfunc_cost_base(libfunc: &StarkNetConcreteLibfunc) -> Vec<Cons
},
}
}
StarkNetConcreteLibfunc::GetClassHashAt(_) => syscall_cost(1),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ pub fn build(
StarkNetConcreteLibfunc::ReplaceClass(_) => {
build_syscalls(builder, "ReplaceClass", [1], [])
}
StarkNetConcreteLibfunc::GetClassHashAt(_) => {
build_syscalls(builder, "GetClassHashAt", [1], [1])
}
StarkNetConcreteLibfunc::SendMessageToL1(_) => {
build_syscalls(builder, "SendMessageToL1", [1, 2], [])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use storage::{
};

pub mod syscalls;
use syscalls::{ReplaceClassLibfunc, SystemType};
use syscalls::{GetClassHashAtLibfunc, ReplaceClassLibfunc, SystemType};

pub mod emit_event;
use emit_event::EmitEventLibfunc;
Expand Down Expand Up @@ -90,6 +90,7 @@ define_libfunc_hierarchy! {
Sha256StateHandleDigest(Sha256StateHandleDigestLibfunc),
LibraryCall(LibraryCallLibfunc),
ReplaceClass(ReplaceClassLibfunc),
GetClassHashAt(GetClassHashAtLibfunc),
SendMessageToL1(SendMessageToL1Libfunc),
Testing(TestingLibfunc),
Secp256(Secp256Libfunc),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::extensions::lib_func::{
SierraApChange, SignatureSpecializationContext,
};
use crate::extensions::modules::get_u256_type;
use crate::extensions::starknet::ContractAddressType;
use crate::extensions::utils::fixed_size_array_ty;
use crate::extensions::{
NamedType, NoGenericArgsGenericLibfunc, NoGenericArgsGenericType, OutputVarReferenceInfo,
Expand Down Expand Up @@ -259,3 +260,22 @@ fn boxed_u32_fixed_array_ty(
let ty = context.get_concrete_type(Uint32Type::id(), &[])?;
box_ty(context, fixed_size_array_ty(context, ty, size)?)
}

/// Libfunc for the get_class_hash_at system call.
#[derive(Default)]
pub struct GetClassHashAtLibfunc {}
impl SyscallGenericLibfunc for GetClassHashAtLibfunc {
const STR_ID: &'static str = "get_class_hash_at_syscall";

fn input_tys(
context: &dyn SignatureSpecializationContext,
) -> Result<Vec<crate::ids::ConcreteTypeId>, SpecializationError> {
Ok(vec![context.get_concrete_type(ContractAddressType::id(), &[])?])
}

fn success_output_tys(
context: &dyn SignatureSpecializationContext,
) -> Result<Vec<crate::ids::ConcreteTypeId>, SpecializationError> {
Ok(vec![context.get_concrete_type(ClassHashType::id(), &[])?])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"get_builtin_costs",
"get_circuit_descriptor",
"get_circuit_output",
"get_class_hash_at_syscall",
"get_execution_info_syscall",
"get_execution_info_v2_syscall",
"hades_permutation",
Expand Down Expand Up @@ -251,4 +252,4 @@
"withdraw_gas",
"withdraw_gas_all"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use starknet::syscalls::{deploy_syscall};
use starknet::class_hash::ClassHash;
use starknet::syscalls::{deploy_syscall, get_class_hash_at_syscall};
use starknet::class_hash::{class_hash_const, ClassHash};
use starknet::contract_address::contract_address_const;

#[starknet::interface]
trait IWithReplace<TContractState> {
Expand Down Expand Up @@ -86,3 +87,30 @@ fn test_cannot_replace_with_non_existing_class_hash() {
let mut contract0 = IWithReplaceDispatcher { contract_address: address0 };
contract0.replace('not a valid class hash'.try_into().unwrap());
}

#[test]
fn test_class_hash_at_syscall() {
let a_class_hash = class_hash_const::<contract_a::TEST_CLASS_HASH>();
// Deploy ContractA with 100 in the storage.
let (address0, _) = deploy_syscall(
class_hash: a_class_hash,
contract_address_salt: 0,
calldata: [100].span(),
deploy_from_zero: false
)
.unwrap();

assert_eq!(get_class_hash_at_syscall(address0), Result::Ok(a_class_hash));
// Replace its class hash to Class B.
let b_class_hash = class_hash_const::<contract_b::TEST_CLASS_HASH>();
IWithReplaceDispatcher { contract_address: address0 }.replace(b_class_hash);
assert_eq!(get_class_hash_at_syscall(address0), Result::Ok(b_class_hash));
}

#[test]
fn test_class_hash_at_syscall_undeployed_contract() {
assert_eq!(
get_class_hash_at_syscall(contract_address_const::<123456>()),
Result::Ok(class_hash_const::<0>())
);
}
73 changes: 73 additions & 0 deletions tests/e2e_test_data/libfuncs/starknet/syscalls
Original file line number Diff line number Diff line change
Expand Up @@ -865,3 +865,76 @@ store_temp<core::result::Result::<(), core::array::Array::<core::felt252>>>([11]
return([6], [7], [11]); // 13

test::foo@0([0]: GasBuiltin, [1]: System, [2]: felt252, [3]: core::array::Span::<core::felt252>) -> (GasBuiltin, System, core::result::Result::<(), core::array::Array::<core::felt252>>);

//! > ==========================================================================

//! > class_hash_at_syscall libfunc

//! > test_comments

//! > test_runner_name
SmallE2ETestRunner

//! > cairo
fn foo(
address: starknet::contract_address::ContractAddress,
) -> starknet::SyscallResult<starknet::class_hash::ClassHash> {
starknet::get_class_hash_at_syscall(address)
}

//! > casm
[ap + 0] = 1448089108044191721802921138471284, ap++;
[ap + -1] = [[fp + -4] + 0];
[fp + -5] = [[fp + -4] + 1];
[fp + -3] = [[fp + -4] + 2];
%{ syscall_handler.syscall(syscall_ptr=memory[fp + -4]) %}
[ap + 0] = [[fp + -4] + 4], ap++;
jmp rel 11 if [ap + -1] != 0;
[ap + 0] = [[fp + -4] + 3], ap++;
[ap + 0] = [fp + -4] + 6, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = 0, ap++;
[ap + 0] = [[fp + -4] + 5], ap++;
ret;
[ap + 0] = [[fp + -4] + 3], ap++;
[ap + 0] = [fp + -4] + 7, ap++;
[ap + 0] = 1, ap++;
[ap + 0] = [[fp + -4] + 5], ap++;
[ap + 0] = [[fp + -4] + 6], ap++;
ret;

//! > function_costs
test::foo: OrderedHashMap({Const: 11100})

//! > sierra_code
type GasBuiltin = GasBuiltin [storable: true, drop: false, dup: false, zero_sized: false];
type ClassHash = ClassHash [storable: true, drop: true, dup: true, zero_sized: false];
type Array<felt252> = Array<felt252> [storable: true, drop: true, dup: false, zero_sized: false];
type core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>> = Enum<ut@core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, ClassHash, Array<felt252>> [storable: true, drop: true, dup: false, zero_sized: false];
type felt252 = felt252 [storable: true, drop: true, dup: true, zero_sized: false];
type ContractAddress = ContractAddress [storable: true, drop: true, dup: true, zero_sized: false];
type System = System [storable: true, drop: false, dup: false, zero_sized: false];

libfunc get_class_hash_at_syscall = get_class_hash_at_syscall;
libfunc branch_align = branch_align;
libfunc enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 0> = enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 0>;
libfunc store_temp<GasBuiltin> = store_temp<GasBuiltin>;
libfunc store_temp<System> = store_temp<System>;
libfunc store_temp<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>> = store_temp<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>>;
libfunc enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 1> = enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 1>;

get_class_hash_at_syscall([0], [1], [2]) { fallthrough([3], [4], [5]) 7([6], [7], [8]) }; // 0
branch_align() -> (); // 1
enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 0>([5]) -> ([9]); // 2
store_temp<GasBuiltin>([3]) -> ([3]); // 3
store_temp<System>([4]) -> ([4]); // 4
store_temp<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>>([9]) -> ([9]); // 5
return([3], [4], [9]); // 6
branch_align() -> (); // 7
enum_init<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>, 1>([8]) -> ([10]); // 8
store_temp<GasBuiltin>([6]) -> ([6]); // 9
store_temp<System>([7]) -> ([7]); // 10
store_temp<core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>>([10]) -> ([10]); // 11
return([6], [7], [10]); // 12

test::foo@0([0]: GasBuiltin, [1]: System, [2]: ContractAddress) -> (GasBuiltin, System, core::result::Result::<core::starknet::class_hash::ClassHash, core::array::Array::<core::felt252>>);

0 comments on commit aee1129

Please sign in to comment.