This repository was archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 52
Add new contracts to include Poseidon for computing class hash #242
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
5678433
Warnings
igamigo 7f27170
Cargo fmt
igamigo c7c4669
fmt
igamigo 9773c64
Migrate to cairo 0.11 (#234)
mmsc2 e0130ba
Rolling back using the deprecated names
igamigo 6ec8052
Fixing VM to use included builtin
igamigo d2fa97f
Removing dead code
igamigo f1779b3
fmt
igamigo 4ddb3f0
Merge branch '0.11' into poseidon-hash
igamigo efbc8ca
Renames fmt
igamigo e0d5baf
Merge branch 'poseidon-hash' of https://github.com/lambdaclass/starkn…
igamigo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, | ||
| ); | ||
| } | ||
igamigo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pefontana marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.