diff --git a/bench/internals.rs b/bench/internals.rs index cce078347..af95e4bd8 100644 --- a/bench/internals.rs +++ b/bench/internals.rs @@ -13,7 +13,7 @@ use starknet_rs::{ internal_invoke_function::InternalInvokeFunction, }, }, - core::contract_address::starknet_contract_address::compute_class_hash, + core::contract_address::starknet_contract_address::compute_deprecated_class_hash, definitions::general_config::StarknetChainId, public::abi::VALIDATE_ENTRY_POINT_SELECTOR, services::api::contract_class::ContractClass, @@ -27,7 +27,7 @@ lazy_static! { "starknet_programs/account_without_validation.json", )) .unwrap(); - static ref CLASS_HASH: [u8; 32] = felt_to_hash(&compute_class_hash( + static ref CLASS_HASH: [u8; 32] = felt_to_hash(&compute_deprecated_class_hash( &CONTRACT_CLASS ).unwrap()); static ref CONTRACT_ADDRESS: Address = Address(felt_str!( diff --git a/cairo_programs/compiled_class.cairo b/cairo_programs/compiled_class.cairo new file mode 100644 index 000000000..20d1701aa --- /dev/null +++ b/cairo_programs/compiled_class.cairo @@ -0,0 +1,263 @@ +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_builtins import PoseidonBuiltin +from starkware.cairo.common.hash_state_poseidon import ( + HashState, + hash_finalize, + hash_init, + hash_update_single, + hash_update_with_nested_hash, +) +from starkware.cairo.common.math import assert_lt_felt +from starkware.cairo.common.registers import get_fp_and_pc + +const COMPILED_CLASS_VERSION = 'COMPILED_CLASS_V1'; + +struct CompiledClassEntryPoint { + // A field element that encodes the signature of the called function. + selector: felt, + // The offset of the instruction that should be called within the contract bytecode. + offset: felt, + // The number of builtins in 'builtin_list'. + n_builtins: felt, + // 'builtin_list' is a continuous memory segment containing the ASCII encoding of the (ordered) + // builtins used by the function. + builtin_list: felt*, +} + +struct CompiledClass { + compiled_class_version: felt, + + // The length and pointer to the external entry point table of the contract. + n_external_functions: felt, + external_functions: CompiledClassEntryPoint*, + + // The length and pointer to the L1 handler entry point table of the contract. + n_l1_handlers: felt, + l1_handlers: CompiledClassEntryPoint*, + + // The length and pointer to the constructor entry point table of the contract. + n_constructors: felt, + constructors: CompiledClassEntryPoint*, + + // The hinted_compiled_class_hash field should be set to the starknet_keccak of the + // contract program, including its hints. However the OS does not validate that. + // This field may be used by the operator to differentiate between contract classes that + // differ only in the hints. + // This field is included in the hash of the CompiledClass to simplify the implementation. + hinted_compiled_class_hash: felt, + + // The length and pointer of the bytecode. + bytecode_length: felt, + bytecode_ptr: felt*, +} + +// Checks that the list of selectors is sorted. +func validate_entry_points{range_check_ptr}( + n_entry_points: felt, entry_points: CompiledClassEntryPoint* +) { + if (n_entry_points == 0) { + return (); + } + + return validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +// Inner function for validate_entry_points. +func validate_entry_points_inner{range_check_ptr}( + n_entry_points: felt, entry_points: CompiledClassEntryPoint*, prev_selector +) { + if (n_entry_points == 0) { + return (); + } + + assert_lt_felt(prev_selector, entry_points[0].selector); + + return validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +func compiled_class_hash{poseidon_ptr: PoseidonBuiltin*}(compiled_class: CompiledClass*) -> ( + hash: felt +) { + let hash_state: HashState = hash_init(); + with hash_state { + hash_update_single(item=compiled_class.compiled_class_version); + + // Hash external entry points. + hash_entry_points( + entry_points=compiled_class.external_functions, + n_entry_points=compiled_class.n_external_functions, + ); + + // Hash L1 handler entry points. + hash_entry_points( + entry_points=compiled_class.l1_handlers, n_entry_points=compiled_class.n_l1_handlers + ); + + // Hash constructor entry points. + hash_entry_points( + entry_points=compiled_class.constructors, n_entry_points=compiled_class.n_constructors + ); + + // Hash hinted_compiled_class_hash. + hash_update_single(item=compiled_class.hinted_compiled_class_hash); + + // Hash bytecode. + hash_update_with_nested_hash( + data_ptr=compiled_class.bytecode_ptr, data_length=compiled_class.bytecode_length + ); + } + + let hash: felt = hash_finalize(hash_state=hash_state); + return (hash=hash); +} + +func hash_entry_points{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}( + entry_points: CompiledClassEntryPoint*, n_entry_points: felt +) { + let inner_hash_state = hash_init(); + hash_entry_points_inner{hash_state=inner_hash_state}( + entry_points=entry_points, n_entry_points=n_entry_points + ); + let hash: felt = hash_finalize(hash_state=inner_hash_state); + hash_update_single(item=hash); + + return (); +} + +func hash_entry_points_inner{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}( + entry_points: CompiledClassEntryPoint*, n_entry_points: felt +) { + if (n_entry_points == 0) { + return (); + } + + hash_update_single(item=entry_points.selector); + hash_update_single(item=entry_points.offset); + + // Hash builtins. + hash_update_with_nested_hash( + data_ptr=entry_points.builtin_list, data_length=entry_points.n_builtins + ); + + return hash_entry_points_inner( + entry_points=&entry_points[1], n_entry_points=n_entry_points - 1 + ); +} + +// A list entry that maps a hash to the corresponding contract classes. +struct CompiledClassFact { + // The hash of the contract. This member should be first, so that we can lookup items + // with the hash as key, using find_element(). + hash: felt, + compiled_class: CompiledClass*, +} + +// Loads the contract classes from the 'os_input' hint variable. +// Returns CompiledClassFact list that maps a hash to a CompiledClass. +func load_compiled_class_facts{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}() -> ( + n_compiled_class_facts: felt, compiled_class_facts: CompiledClassFact* +) { + alloc_locals; + local n_compiled_class_facts; + local compiled_class_facts: CompiledClassFact*; + %{ + ids.compiled_class_facts = segments.add() + ids.n_compiled_class_facts = len(os_input.compiled_classes) + vm_enter_scope({ + 'compiled_class_facts': iter(os_input.compiled_classes.items()), + }) + %} + + let (builtin_costs: felt*) = alloc(); + assert builtin_costs[0] = 0; + assert builtin_costs[1] = 0; + assert builtin_costs[2] = 0; + assert builtin_costs[3] = 0; + assert builtin_costs[4] = 0; + + load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts, + compiled_class_facts=compiled_class_facts, + builtin_costs=builtin_costs, + ); + %{ vm_exit_scope() %} + + return ( + n_compiled_class_facts=n_compiled_class_facts, compiled_class_facts=compiled_class_facts + ); +} + +// Loads 'n_compiled_class_facts' from the hint 'compiled_class_facts' and appends the +// corresponding CompiledClassFact to compiled_class_facts. +func load_compiled_class_facts_inner{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}( + n_compiled_class_facts, compiled_class_facts: CompiledClassFact*, builtin_costs: felt* +) { + if (n_compiled_class_facts == 0) { + return (); + } + alloc_locals; + + let compiled_class_fact = compiled_class_facts[0]; + let compiled_class = compiled_class_fact.compiled_class; + + // Fetch contract data form hints. + %{ + from starkware.starknet.core.os.contract_class.compiled_class_hash import ( + get_compiled_class_struct, + ) + + compiled_class_hash, compiled_class = next(compiled_class_facts) + + cairo_contract = get_compiled_class_struct( + identifiers=ids._context.identifiers, compiled_class=compiled_class) + ids.compiled_class = segments.gen_arg(cairo_contract) + %} + + assert compiled_class.compiled_class_version = COMPILED_CLASS_VERSION; + + validate_entry_points( + n_entry_points=compiled_class.n_external_functions, + entry_points=compiled_class.external_functions, + ); + + validate_entry_points( + n_entry_points=compiled_class.n_l1_handlers, entry_points=compiled_class.l1_handlers + ); + + let (hash) = compiled_class_hash(compiled_class); + compiled_class_fact.hash = hash; + + // Compiled classes are expected to end with a `ret` opcode followed by a pointer to the + // builtin costs. + assert compiled_class.bytecode_ptr[compiled_class.bytecode_length] = 0x208b7fff7fff7ffe; + assert compiled_class.bytecode_ptr[compiled_class.bytecode_length + 1] = cast( + builtin_costs, felt + ); + + %{ + computed_hash = ids.compiled_class_fact.hash + expected_hash = compiled_class_hash + assert computed_hash == expected_hash, ( + "Computed compiled_class_hash is inconsistent with the hash in the os_input. " + f"Computed hash = {computed_hash}, Expected hash = {expected_hash}.") + + vm_load_program( + compiled_class.get_runnable_program(entrypoint_builtins=[]), + ids.compiled_class.bytecode_ptr + ) + %} + + return load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts - 1, + compiled_class_facts=compiled_class_facts + CompiledClassFact.SIZE, + builtin_costs=builtin_costs, + ); +} \ No newline at end of file diff --git a/cairo_programs/contract_class.cairo b/cairo_programs/contract_class.cairo new file mode 100644 index 000000000..485e4f8ea --- /dev/null +++ b/cairo_programs/contract_class.cairo @@ -0,0 +1,77 @@ +from starkware.cairo.common.cairo_builtins import PoseidonBuiltin +from starkware.cairo.common.hash_state_poseidon import ( + HashState, + hash_finalize, + hash_init, + hash_update_single, + hash_update_with_nested_hash, +) + +const CONTRACT_CLASS_VERSION = 'CONTRACT_CLASS_V0.1.0'; + +struct ContractEntryPoint { + // A field element that encodes the signature of the called function. + selector: felt, + function_idx: felt, +} + +struct ContractClass { + contract_class_version: felt, + + // The length and pointer to the external entry point table of the contract. + n_external_functions: felt, + external_functions: ContractEntryPoint*, + + // The length and pointer to the L1 handler entry point table of the contract. + n_l1_handlers: felt, + l1_handlers: ContractEntryPoint*, + + // The length and pointer to the constructor entry point table of the contract. + n_constructors: felt, + constructors: ContractEntryPoint*, + + // starknet_keccak of the contract ABI. + // Note that the OS does not enforce any constraints on this value. + abi_hash: felt, + + // The length and pointer of the Sierra program. + sierra_program_length: felt, + sierra_program_ptr: felt*, +} + +func class_hash{poseidon_ptr: PoseidonBuiltin*}(contract_class: ContractClass*) -> (hash: felt) { + let hash_state: HashState = hash_init(); + with hash_state { + hash_update_single(item=contract_class.contract_class_version); + + // Hash external entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.external_functions, + data_length=contract_class.n_external_functions * ContractEntryPoint.SIZE, + ); + + // Hash L1 handler entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.l1_handlers, + data_length=contract_class.n_l1_handlers * ContractEntryPoint.SIZE, + ); + + // Hash constructor entry points. + hash_update_with_nested_hash( + data_ptr=contract_class.constructors, + data_length=contract_class.n_constructors * ContractEntryPoint.SIZE, + ); + + // Hash abi_hash. + hash_update_single(item=contract_class.abi_hash); + + // Hash Sierra program. + hash_update_with_nested_hash( + data_ptr=contract_class.sierra_program_ptr, + data_length=contract_class.sierra_program_length, + ); + + let hash: felt = hash_finalize(hash_state=hash_state); + } + return (hash=hash); +} diff --git a/cairo_programs/contracts.cairo b/cairo_programs/contracts.cairo deleted file mode 100644 index 4fa0170f5..000000000 --- a/cairo_programs/contracts.cairo +++ /dev/null @@ -1,224 +0,0 @@ -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.hash_state import ( - HashState, - hash_finalize, - hash_init, - hash_update, - hash_update_single, - hash_update_with_hashchain, -) -from starkware.cairo.common.math import assert_lt_felt -from starkware.cairo.common.registers import get_fp_and_pc - -const API_VERSION = 0; - -struct ContractEntryPoint { - // A field element that encodes the signature of the called function. - selector: felt, - // The offset of the instruction that should be called within the contract bytecode. - offset: felt, -} - -struct ContractClass { - api_version: felt, - - // The length and pointer to the external entry point table of the contract. - n_external_functions: felt, - external_functions: ContractEntryPoint*, - - // The length and pointer to the L1 handler entry point table of the contract. - n_l1_handlers: felt, - l1_handlers: ContractEntryPoint*, - - // The length and pointer to the constructor entry point table of the contract. - n_constructors: felt, - constructors: ContractEntryPoint*, - - n_builtins: felt, - // 'builtin_list' is a continuous memory segment containing the ASCII encoding of the (ordered) - // builtins used by the program. - builtin_list: felt*, - - // The hinted_class_hash field should be set to the starknet_keccak of the - // contract program, including its hints. However the OS does not validate that. - // This field may be used by the operator to differentiate between contract classes that - // differ only in the hints. - // This field is included in the hash of the ContractClass to simplify the implementation. - hinted_class_hash: felt, - - // The length and pointer of the bytecode. - bytecode_length: felt, - bytecode_ptr: felt*, -} - -// Checks that the list of selectors is sorted. -func validate_entry_points{range_check_ptr}( - n_entry_points: felt, entry_points: ContractEntryPoint* -) { - if (n_entry_points == 0) { - return (); - } - - return validate_entry_points_inner( - n_entry_points=n_entry_points - 1, - entry_points=&entry_points[1], - prev_selector=entry_points[0].selector, - ); -} - -// Inner function for validate_entry_points. -func validate_entry_points_inner{range_check_ptr}( - n_entry_points: felt, entry_points: ContractEntryPoint*, prev_selector -) { - if (n_entry_points == 0) { - return (); - } - - assert_lt_felt(prev_selector, entry_points.selector); - - return validate_entry_points_inner( - n_entry_points=n_entry_points - 1, - entry_points=&entry_points[1], - prev_selector=entry_points[0].selector, - ); -} - -func class_hash{hash_ptr: HashBuiltin*}(contract_class: ContractClass*) -> (hash: felt) { - let (hash_state_ptr: HashState*) = hash_init(); - let (hash_state_ptr) = hash_update_single( - hash_state_ptr=hash_state_ptr, item=contract_class.api_version - ); - - // Hash external entry points. - let (hash_state_ptr) = hash_update_with_hashchain( - hash_state_ptr=hash_state_ptr, - data_ptr=contract_class.external_functions, - data_length=contract_class.n_external_functions * ContractEntryPoint.SIZE, - ); - - // Hash L1 handler entry points. - let (hash_state_ptr) = hash_update_with_hashchain( - hash_state_ptr=hash_state_ptr, - data_ptr=contract_class.l1_handlers, - data_length=contract_class.n_l1_handlers * ContractEntryPoint.SIZE, - ); - - // Hash constructor entry points. - let (hash_state_ptr) = hash_update_with_hashchain( - hash_state_ptr=hash_state_ptr, - data_ptr=contract_class.constructors, - data_length=contract_class.n_constructors * ContractEntryPoint.SIZE, - ); - - // Hash builtins. - let (hash_state_ptr) = hash_update_with_hashchain( - hash_state_ptr=hash_state_ptr, - data_ptr=contract_class.builtin_list, - data_length=contract_class.n_builtins, - ); - - // Hash hinted_class_hash. - let (hash_state_ptr) = hash_update_single( - hash_state_ptr=hash_state_ptr, item=contract_class.hinted_class_hash - ); - - // Hash bytecode. - let (hash_state_ptr) = hash_update_with_hashchain( - hash_state_ptr=hash_state_ptr, - data_ptr=contract_class.bytecode_ptr, - data_length=contract_class.bytecode_length, - ); - - let (hash: felt) = hash_finalize(hash_state_ptr=hash_state_ptr); - return (hash=hash); -} - -// A list entry that maps a hash to the corresponding contract classes. -struct ContractClassFact { - // The hash of the contract. This member should be first, so that we can lookup items - // with the hash as key, using find_element(). - hash: felt, - contract_class: ContractClass*, -} - -// Loads the contract classes from the 'os_input' hint variable. -// Returns ContractClassFact list that maps a hash to a ContractClass. -func load_contract_class_facts{pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( - n_contract_class_facts: felt, contract_class_facts: ContractClassFact* -) { - alloc_locals; - local n_contract_class_facts; - local contract_class_facts: ContractClassFact*; - %{ - ids.contract_class_facts = segments.add() - ids.n_contract_class_facts = len(os_input.contract_definitions) - vm_enter_scope({ - 'contract_class_facts': iter(os_input.contract_definitions.items()), - }) - %} - - load_contract_class_facts_inner( - n_contract_class_facts=n_contract_class_facts, contract_class_facts=contract_class_facts - ); - %{ vm_exit_scope() %} - - return ( - n_contract_class_facts=n_contract_class_facts, contract_class_facts=contract_class_facts - ); -} - -// Loads 'n_contract_class_facts' from the hint 'contract_class_facts' and appends the -// corresponding ContractClassFact to contract_class_facts. -func load_contract_class_facts_inner{pedersen_ptr: HashBuiltin*, range_check_ptr}( - n_contract_class_facts, contract_class_facts: ContractClassFact* -) { - if (n_contract_class_facts == 0) { - return (); - } - alloc_locals; - - let contract_class_fact = contract_class_facts; - let contract_class = contract_class_fact.contract_class; - - // Fetch contract data form hints. - %{ - from starkware.starknet.core.os.class_hash import get_contract_class_struct - - class_hash, contract_class = next(contract_class_facts) - - cairo_contract = get_contract_class_struct( - identifiers=ids._context.identifiers, contract_class=contract_class) - ids.contract_class = segments.gen_arg(cairo_contract) - %} - - assert contract_class.api_version = API_VERSION; - - validate_entry_points( - n_entry_points=contract_class.n_external_functions, - entry_points=contract_class.external_functions, - ); - - validate_entry_points( - n_entry_points=contract_class.n_l1_handlers, entry_points=contract_class.l1_handlers - ); - - let (hash) = class_hash{hash_ptr=pedersen_ptr}(contract_class); - contract_class_fact.hash = hash; - - %{ - from starkware.python.utils import from_bytes - - computed_hash = ids.contract_class_fact.hash - expected_hash = from_bytes(class_hash) - assert computed_hash == expected_hash, ( - "Computed class_hash is inconsistent with the hash in the os_input. " - f"Computed hash = {computed_hash}, Expected hash = {expected_hash}.") - - vm_load_program(contract_class.program, ids.contract_class.bytecode_ptr) - %} - - return load_contract_class_facts_inner( - n_contract_class_facts=n_contract_class_facts - 1, - contract_class_facts=contract_class_facts + ContractClassFact.SIZE, - ); -} diff --git a/cairo_programs/deprecated_compiled_class.cairo b/cairo_programs/deprecated_compiled_class.cairo new file mode 100644 index 000000000..3fec3fdc4 --- /dev/null +++ b/cairo_programs/deprecated_compiled_class.cairo @@ -0,0 +1,230 @@ +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash_state import ( + HashState, + hash_finalize, + hash_init, + hash_update, + hash_update_single, + hash_update_with_hashchain, +) +from starkware.cairo.common.math import assert_lt_felt +from starkware.cairo.common.registers import get_fp_and_pc + +const DEPRECATED_COMPILED_CLASS_VERSION = 0; + +struct DeprecatedContractEntryPoint { + // A field element that encodes the signature of the called function. + selector: felt, + // The offset of the instruction that should be called within the contract bytecode. + offset: felt, +} + +struct DeprecatedCompiledClass { + compiled_class_version: felt, + + // The length and pointer to the external entry point table of the contract. + n_external_functions: felt, + external_functions: DeprecatedContractEntryPoint*, + + // The length and pointer to the L1 handler entry point table of the contract. + n_l1_handlers: felt, + l1_handlers: DeprecatedContractEntryPoint*, + + // The length and pointer to the constructor entry point table of the contract. + n_constructors: felt, + constructors: DeprecatedContractEntryPoint*, + + n_builtins: felt, + // 'builtin_list' is a continuous memory segment containing the ASCII encoding of the (ordered) + // builtins used by the program. + builtin_list: felt*, + + // The hinted_class_hash field should be set to the starknet_keccak of the + // contract program, including its hints. However the OS does not validate that. + // This field may be used by the operator to differentiate between contract classes that + // differ only in the hints. + // This field is included in the hash of the ContractClass to simplify the implementation. + hinted_class_hash: felt, + + // The length and pointer of the bytecode. + bytecode_length: felt, + bytecode_ptr: felt*, +} + +// Checks that the list of selectors is sorted. +func deprecated_validate_entry_points{range_check_ptr}( + n_entry_points: felt, entry_points: DeprecatedContractEntryPoint* +) { + if (n_entry_points == 0) { + return (); + } + + return deprecated_validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +// Inner function for deprecated_validate_entry_points. +func deprecated_validate_entry_points_inner{range_check_ptr}( + n_entry_points: felt, entry_points: DeprecatedContractEntryPoint*, prev_selector +) { + if (n_entry_points == 0) { + return (); + } + + assert_lt_felt(prev_selector, entry_points.selector); + + return deprecated_validate_entry_points_inner( + n_entry_points=n_entry_points - 1, + entry_points=&entry_points[1], + prev_selector=entry_points[0].selector, + ); +} + +func deprecated_compiled_class_hash{hash_ptr: HashBuiltin*}( + compiled_class: DeprecatedCompiledClass* +) -> (hash: felt) { + let (hash_state_ptr: HashState*) = hash_init(); + let (hash_state_ptr) = hash_update_single( + hash_state_ptr=hash_state_ptr, item=compiled_class.compiled_class_version + ); + + // Hash external entry points. + let (hash_state_ptr) = hash_update_with_hashchain( + hash_state_ptr=hash_state_ptr, + data_ptr=compiled_class.external_functions, + data_length=compiled_class.n_external_functions * DeprecatedContractEntryPoint.SIZE, + ); + + // Hash L1 handler entry points. + let (hash_state_ptr) = hash_update_with_hashchain( + hash_state_ptr=hash_state_ptr, + data_ptr=compiled_class.l1_handlers, + data_length=compiled_class.n_l1_handlers * DeprecatedContractEntryPoint.SIZE, + ); + + // Hash constructor entry points. + let (hash_state_ptr) = hash_update_with_hashchain( + hash_state_ptr=hash_state_ptr, + data_ptr=compiled_class.constructors, + data_length=compiled_class.n_constructors * DeprecatedContractEntryPoint.SIZE, + ); + + // Hash builtins. + let (hash_state_ptr) = hash_update_with_hashchain( + hash_state_ptr=hash_state_ptr, + data_ptr=compiled_class.builtin_list, + data_length=compiled_class.n_builtins, + ); + + // Hash hinted_class_hash. + let (hash_state_ptr) = hash_update_single( + hash_state_ptr=hash_state_ptr, item=compiled_class.hinted_class_hash + ); + + // Hash bytecode. + let (hash_state_ptr) = hash_update_with_hashchain( + hash_state_ptr=hash_state_ptr, + data_ptr=compiled_class.bytecode_ptr, + data_length=compiled_class.bytecode_length, + ); + + let (hash: felt) = hash_finalize(hash_state_ptr=hash_state_ptr); + return (hash=hash); +} + +// A list entry that maps a hash to the corresponding compiled classes. +struct DeprecatedCompiledClassFact { + // The hash of the compiled class. This member should be first, so that we can lookup items + // with the hash as key, using find_element(). + hash: felt, + compiled_class: DeprecatedCompiledClass*, +} + +// Loads the compiled classes from the 'os_input' hint variable. +// Returns DeprecatedCompiledClassFact list that maps a hash to a DeprecatedCompiledClass. +func deprecated_load_compiled_class_facts{pedersen_ptr: HashBuiltin*, range_check_ptr}() -> ( + n_compiled_class_facts: felt, compiled_class_facts: DeprecatedCompiledClassFact* +) { + alloc_locals; + local n_compiled_class_facts; + local compiled_class_facts: DeprecatedCompiledClassFact*; + %{ + # Creates a set of deprecated class hashes to distinguish calls to deprecated entry points. + __deprecated_class_hashes=set(os_input.deprecated_compiled_classes.keys()) + ids.compiled_class_facts = segments.add() + ids.n_compiled_class_facts = len(os_input.deprecated_compiled_classes) + vm_enter_scope({ + 'compiled_class_facts': iter(os_input.deprecated_compiled_classes.items()), + }) + %} + + deprecated_load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts, compiled_class_facts=compiled_class_facts + ); + %{ vm_exit_scope() %} + + return ( + n_compiled_class_facts=n_compiled_class_facts, compiled_class_facts=compiled_class_facts + ); +} + +// Loads 'n_compiled_class_facts' from the hint 'compiled_class_facts' and appends the +// corresponding DeprecatedCompiledClassFact to compiled_class_facts. +func deprecated_load_compiled_class_facts_inner{pedersen_ptr: HashBuiltin*, range_check_ptr}( + n_compiled_class_facts, compiled_class_facts: DeprecatedCompiledClassFact* +) { + if (n_compiled_class_facts == 0) { + return (); + } + alloc_locals; + + let compiled_class_fact = compiled_class_facts; + let compiled_class = compiled_class_fact.compiled_class; + + // Fetch contract data form hints. + %{ + from starkware.starknet.core.os.contract_class.deprecated_class_hash import ( + get_deprecated_contract_class_struct, + ) + + compiled_class_hash, compiled_class = next(compiled_class_facts) + + cairo_contract = get_deprecated_contract_class_struct( + identifiers=ids._context.identifiers, contract_class=compiled_class) + ids.compiled_class = segments.gen_arg(cairo_contract) + %} + + assert compiled_class.compiled_class_version = DEPRECATED_COMPILED_CLASS_VERSION; + + deprecated_validate_entry_points( + n_entry_points=compiled_class.n_external_functions, + entry_points=compiled_class.external_functions, + ); + + deprecated_validate_entry_points( + n_entry_points=compiled_class.n_l1_handlers, entry_points=compiled_class.l1_handlers + ); + + let (hash) = deprecated_compiled_class_hash{hash_ptr=pedersen_ptr}(compiled_class); + compiled_class_fact.hash = hash; + + %{ + from starkware.python.utils import from_bytes + + computed_hash = ids.compiled_class_fact.hash + expected_hash = compiled_class_hash + assert computed_hash == expected_hash, ( + "Computed compiled_class_hash is inconsistent with the hash in the os_input. " + f"Computed hash = {computed_hash}, Expected hash = {expected_hash}.") + + vm_load_program(compiled_class.program, ids.compiled_class.bytecode_ptr) + %} + + return deprecated_load_compiled_class_facts_inner( + n_compiled_class_facts=n_compiled_class_facts - 1, + compiled_class_facts=compiled_class_facts + DeprecatedCompiledClassFact.SIZE, + ); +} diff --git a/fuzzer/Cargo.toml b/fuzzer/Cargo.toml index 790025415..a969b415c 100644 --- a/fuzzer/Cargo.toml +++ b/fuzzer/Cargo.toml @@ -8,12 +8,12 @@ edition = "2021" [dependencies] honggfuzz = "0.5.55" starknet-rs = { path = "../" } -felt = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-felt", rev = "8dba86dbec935fa04a255e2edf3d5d184950fa22" } +felt = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-felt", rev = "455a1e8d87b3f6cfda72255d93d57d1c49f239a3" } num-traits = "0.2.15" starknet_api = { git = "https://github.com/lambdaclass/starknet-api", branch = "main", features = ["testing"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } tempfile = "3.2.0" -cairo-rs = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-vm", rev = "8dba86dbec935fa04a255e2edf3d5d184950fa22" } +cairo-rs = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-vm", rev = "455a1e8d87b3f6cfda72255d93d57d1c49f239a3" } [profile.release] strip = "debuginfo" diff --git a/src/business_logic/transaction/objects/internal_declare.rs b/src/business_logic/transaction/objects/internal_declare.rs index a0b56f8dd..753f2db73 100644 --- a/src/business_logic/transaction/objects/internal_declare.rs +++ b/src/business_logic/transaction/objects/internal_declare.rs @@ -12,7 +12,7 @@ use crate::{ }, }, core::{ - contract_address::starknet_contract_address::compute_class_hash, + contract_address::starknet_contract_address::compute_deprecated_class_hash, errors::state_errors::StateError, transaction_hash::starknet_transaction_hash::calculate_declare_transaction_hash, }, @@ -59,7 +59,7 @@ impl InternalDeclare { signature: Vec, nonce: Felt252, ) -> Result { - let hash = compute_class_hash(&contract_class)?; + let hash = compute_deprecated_class_hash(&contract_class)?; let class_hash = felt_to_hash(&hash); let hash_value = calculate_declare_transaction_hash( @@ -326,7 +326,7 @@ mod tests { let mut contract_class_cache = HashMap::new(); // ------------ contract data -------------------- - let hash = compute_class_hash(&contract_class).unwrap(); + let hash = compute_deprecated_class_hash(&contract_class).unwrap(); let class_hash = felt_to_hash(&hash); contract_class_cache.insert(class_hash, contract_class.clone()); @@ -375,7 +375,7 @@ mod tests { // Value generated from selector _validate_declare_ let entry_point_selector = Some(VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone()); - let class_hash_felt = compute_class_hash(&contract_class).unwrap(); + let class_hash_felt = compute_deprecated_class_hash(&contract_class).unwrap(); let expected_class_hash = felt_to_hash(&class_hash_felt); // Calldata is the class hash represented as a Felt252 diff --git a/src/business_logic/transaction/objects/internal_deploy.rs b/src/business_logic/transaction/objects/internal_deploy.rs index 7782967d1..fe591ffeb 100644 --- a/src/business_logic/transaction/objects/internal_deploy.rs +++ b/src/business_logic/transaction/objects/internal_deploy.rs @@ -9,7 +9,7 @@ use crate::{ transaction::error::TransactionError, }, core::{ - contract_address::starknet_contract_address::compute_class_hash, + contract_address::starknet_contract_address::compute_deprecated_class_hash, errors::syscall_handler_errors::SyscallHandlerError, transaction_hash::starknet_transaction_hash::calculate_deploy_transaction_hash, }, @@ -43,7 +43,7 @@ impl InternalDeploy { chain_id: Felt252, version: u64, ) -> Result { - let class_hash = compute_class_hash(&contract_class) + let class_hash = compute_deprecated_class_hash(&contract_class) .map_err(|_| SyscallHandlerError::ErrorComputingHash)?; let contract_hash: ClassHash = felt_to_hash(&class_hash); diff --git a/src/core/contract_address/starknet_contract_address.rs b/src/core/contract_address/starknet_contract_address.rs index f21061084..f63449f83 100644 --- a/src/core/contract_address/starknet_contract_address.rs +++ b/src/core/contract_address/starknet_contract_address.rs @@ -1,3 +1,5 @@ +/// Contains functionality for computing class hashes for deprecated Declare transactions +/// (ie, declarations that do not correspond to Cairo 1 contracts) use crate::{ core::errors::contract_address_errors::ContractAddressError, services::api::contract_class::{ContractClass, ContractEntryPoint, EntryPointType}, @@ -7,7 +9,10 @@ use cairo_rs::{ serde::deserialize_program::Identifier, types::{program::Program, relocatable::MaybeRelocatable}, vm::{ - runners::cairo_runner::{CairoArg, CairoRunner}, + runners::{ + builtin_runner::BuiltinRunner, + cairo_runner::{CairoArg, CairoRunner}, + }, vm_core::VirtualMachine, }, }; @@ -20,7 +25,7 @@ pub const MASK_3: u8 = 3; fn load_program() -> Result { Ok(Program::from_file( - Path::new("cairo_programs/contracts.json"), + Path::new("cairo_programs/deprecated_compiled_class.json"), None, )?) } @@ -77,17 +82,21 @@ fn compute_hinted_class_hash(_contract_class: &ContractClass) -> Felt252 { fn get_contract_class_struct( identifiers: &HashMap, contract_class: &ContractClass, -) -> Result { - let api_version = identifiers.get("__main__.API_VERSION").ok_or_else(|| { - ContractAddressError::MissingIdentifier("__main__.API_VERSION".to_string()) - })?; +) -> Result { + let api_version = identifiers + .get("__main__.DEPRECATED_COMPILED_CLASS_VERSION") + .ok_or_else(|| { + ContractAddressError::MissingIdentifier( + "__main__.DEPRECATED_COMPILED_CLASS_VERSION".to_string(), + ) + })?; let external_functions = get_contract_entry_points(contract_class, &EntryPointType::External)?; let l1_handlers = get_contract_entry_points(contract_class, &EntryPointType::L1Handler)?; let constructors = get_contract_entry_points(contract_class, &EntryPointType::Constructor)?; let builtin_list = &contract_class.program.builtins; - Ok(StructContractClass { - api_version: api_version + Ok(DeprecatedCompiledClass { + compiled_class_version: api_version .value .as_ref() .ok_or(ContractAddressError::NoneApiVersion)? @@ -112,10 +121,9 @@ fn get_contract_class_struct( }) } -// TODO: think about a new name for this struct (ContractClass already exists) #[derive(Debug)] -struct StructContractClass { - api_version: MaybeRelocatable, +struct DeprecatedCompiledClass { + compiled_class_version: MaybeRelocatable, n_external_functions: MaybeRelocatable, external_functions: Vec, n_l1_handlers: MaybeRelocatable, @@ -136,14 +144,14 @@ fn flat_into_maybe_relocs(contract_entrypoints: Vec) -> Vec< .collect::>() } -impl From for CairoArg { - fn from(contract_class: StructContractClass) -> Self { +impl From for CairoArg { + fn from(contract_class: DeprecatedCompiledClass) -> Self { let external_functions_flatted = flat_into_maybe_relocs(contract_class.external_functions); let l1_handlers_flatted = flat_into_maybe_relocs(contract_class.l1_handlers); let constructors_flatted = flat_into_maybe_relocs(contract_class.constructors); let result = vec![ - CairoArg::Single(contract_class.api_version), + CairoArg::Single(contract_class.compiled_class_version), CairoArg::Single(contract_class.n_external_functions), CairoArg::Array(external_functions_flatted), CairoArg::Single(contract_class.n_l1_handlers), @@ -161,7 +169,9 @@ impl From for CairoArg { } // TODO: Maybe this could be hard-coded (to avoid returning a result)? -pub fn compute_class_hash(contract_class: &ContractClass) -> Result { +pub fn compute_deprecated_class_hash( + contract_class: &ContractClass, +) -> Result { // Since we are not using a cache, this function replace compute_class_hash_inner. let program = load_program()?; let contract_class_struct = @@ -172,14 +182,22 @@ pub fn compute_class_hash(contract_class: &ContractClass) -> Result Result { - let class_hash = - compute_class_hash(contract_class).map_err(|_| SyscallHandlerError::FailToComputeHash)?; + let class_hash = compute_deprecated_class_hash(contract_class) + .map_err(|_| SyscallHandlerError::FailToComputeHash)?; let (calldata, additional_data) = if version > 0 { (Vec::new(), vec![class_hash]) diff --git a/src/main.rs b/src/main.rs index 44070909c..5bd4cdccc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use starknet_rs::{ transaction::objects::internal_invoke_function::InternalInvokeFunction, }, core::{ - contract_address::starknet_contract_address::compute_class_hash, + contract_address::starknet_contract_address::compute_deprecated_class_hash, transaction_hash::starknet_transaction_hash::{ calculate_declare_transaction_hash, calculate_deploy_transaction_hash, calculate_transaction_hash_common, TransactionHashPrefix, @@ -107,7 +107,7 @@ fn declare_parser( args: &DeclareArgs, ) -> Result<(Felt252, Felt252), ParserError> { let contract_class = ContractClass::try_from(&args.contract)?; - let class_hash = compute_class_hash(&contract_class)?; + let class_hash = compute_deprecated_class_hash(&contract_class)?; cached_state.set_contract_class(&felt_to_hash(&class_hash), &contract_class)?; let tx_hash = calculate_declare_transaction_hash( diff --git a/src/starknet_runner/runner.rs b/src/starknet_runner/runner.rs index 0d9d2466e..fe1f74efe 100644 --- a/src/starknet_runner/runner.rs +++ b/src/starknet_runner/runner.rs @@ -107,10 +107,10 @@ where let builtin_runners = self .vm .get_builtin_runners() - .clone() - .into_iter() - .map(|b| (b.name(), b)) + .iter() + .map(|runner| (runner.name(), runner.clone())) .collect::>(); + self.cairo_runner .get_program_builtins() .iter() diff --git a/src/testing/starknet_state.rs b/src/testing/starknet_state.rs index b28ea1d78..4dc5739c5 100644 --- a/src/testing/starknet_state.rs +++ b/src/testing/starknet_state.rs @@ -274,7 +274,7 @@ mod tests { use crate::{ business_logic::{execution::objects::CallType, state::state_cache::StorageEntry}, - core::contract_address::starknet_contract_address::compute_class_hash, + core::contract_address::starknet_contract_address::compute_deprecated_class_hash, definitions::{ constants::CONSTRUCTOR_ENTRY_POINT_SELECTOR, transaction_type::TransactionType, }, @@ -293,7 +293,7 @@ mod tests { // expected results // ----- calculate fib class hash --------- - let hash = compute_class_hash(&contract_class).unwrap(); + let hash = compute_deprecated_class_hash(&contract_class).unwrap(); let class_hash = felt_to_hash(&hash); let address = Address(felt_str!( @@ -353,7 +353,7 @@ mod tests { // ------------ contract data -------------------- // hack store account contract - let hash = compute_class_hash(&contract_class).unwrap(); + let hash = compute_deprecated_class_hash(&contract_class).unwrap(); let class_hash = felt_to_hash(&hash); contract_class_cache.insert(class_hash, contract_class.clone()); @@ -425,7 +425,7 @@ mod tests { //* --------------------------------------- // ----- calculate fib class hash --------- - let hash = compute_class_hash(&fib_contract_class).unwrap(); + let hash = compute_deprecated_class_hash(&fib_contract_class).unwrap(); let fib_class_hash = felt_to_hash(&hash); // check that it return the correct clash hash @@ -492,7 +492,7 @@ mod tests { // expected result // ----- calculate fib class hash --------- - let hash = compute_class_hash(&contract_class).unwrap(); + let hash = compute_deprecated_class_hash(&contract_class).unwrap(); let fib_class_hash = felt_to_hash(&hash); let address = felt_str!( diff --git a/tests/deploy_account.rs b/tests/deploy_account.rs index 2da194e25..b01d06e2a 100644 --- a/tests/deploy_account.rs +++ b/tests/deploy_account.rs @@ -9,7 +9,7 @@ use starknet_rs::{ state::{cached_state::CachedState, state_api::State}, transaction::objects::internal_deploy_account::InternalDeployAccount, }, - core::contract_address::starknet_contract_address::compute_class_hash, + core::contract_address::starknet_contract_address::compute_deprecated_class_hash, definitions::{ constants::CONSTRUCTOR_ENTRY_POINT_SELECTOR, general_config::StarknetChainId, transaction_type::TransactionType, @@ -31,7 +31,7 @@ fn internal_deploy_account() { )) .unwrap(); - let class_hash = felt_to_hash(&compute_class_hash(&contract_class).unwrap()); + let class_hash = felt_to_hash(&compute_deprecated_class_hash(&contract_class).unwrap()); state .set_contract_class(&class_hash, &contract_class)