1- from typing import List
1+ from typing import List , Optional , Sequence , Tuple
22
33from poseidon_py .poseidon_hash import poseidon_hash_many
44
55from starknet_py .cairo .felt import encode_shortstring
6+ from starknet_py .hash .compiled_class_hash_objects import (
7+ BytecodeLeaf ,
8+ BytecodeSegment ,
9+ BytecodeSegmentedNode ,
10+ BytecodeSegmentStructure ,
11+ NestedIntList ,
12+ )
613from starknet_py .net .client_models import CasmClass , CasmClassEntryPoint
714
815CASM_CLASS_VERSION = "COMPILED_CLASS_V1"
@@ -26,7 +33,14 @@ def compute_casm_class_hash(casm_contract_class: CasmClass) -> int:
2633 _entry_points_array (_entry_points .constructor )
2734 )
2835
29- bytecode_hash = poseidon_hash_many (casm_contract_class .bytecode )
36+ if casm_contract_class .bytecode_segment_lengths is not None :
37+ bytecode_hash = create_bytecode_segment_structure (
38+ bytecode = casm_contract_class .bytecode ,
39+ bytecode_segment_lengths = casm_contract_class .bytecode_segment_lengths ,
40+ visited_pcs = None ,
41+ ).hash ()
42+ else :
43+ bytecode_hash = poseidon_hash_many (casm_contract_class .bytecode )
3044
3145 return poseidon_hash_many (
3246 [
@@ -51,3 +65,90 @@ def _entry_points_array(entry_points: List[CasmClassEntryPoint]) -> List[int]:
5165 )
5266
5367 return entry_points_array
68+
69+
70+ # create_bytecode_segment_structure and _create_bytecode_segment_structure_inner are copied from
71+ # https://github.com/starkware-libs/cairo-lang/blob/v0.13.1/src/starkware/starknet/core/os/contract_class/compiled_class_hash.py
72+
73+
74+ def create_bytecode_segment_structure (
75+ bytecode : List [int ],
76+ bytecode_segment_lengths : NestedIntList ,
77+ visited_pcs : Optional [Sequence [int ]],
78+ ) -> BytecodeSegmentStructure :
79+ """
80+ Creates a BytecodeSegmentStructure instance from the given bytecode and
81+ bytecode_segment_lengths.
82+ """
83+ rev_visited_pcs = list (
84+ visited_pcs if visited_pcs is not None else range (len (bytecode ))
85+ )[::- 1 ]
86+
87+ res , total_len = _create_bytecode_segment_structure_inner (
88+ bytecode = bytecode ,
89+ bytecode_segment_lengths = bytecode_segment_lengths ,
90+ visited_pcs = rev_visited_pcs ,
91+ bytecode_offset = 0 ,
92+ )
93+ assert total_len == len (
94+ bytecode
95+ ), f"Invalid length bytecode segment structure: { total_len } . Bytecode length: { len (bytecode )} ."
96+ assert len (rev_visited_pcs ) == 0 , f"PC { rev_visited_pcs [- 1 ]} is out of range."
97+ return res
98+
99+
100+ def _create_bytecode_segment_structure_inner (
101+ bytecode : List [int ],
102+ bytecode_segment_lengths : NestedIntList ,
103+ visited_pcs : List [int ],
104+ bytecode_offset : int ,
105+ ) -> Tuple [BytecodeSegmentStructure , int ]:
106+ """
107+ Helper function for `create_bytecode_segment_structure`.
108+ `visited_pcs` should be given in reverse order, and is consumed by the function.
109+ Returns the BytecodeSegmentStructure and the total length of the processed segment.
110+ """
111+ if isinstance (bytecode_segment_lengths , int ):
112+ segment_end = bytecode_offset + bytecode_segment_lengths
113+
114+ # Remove all the visited PCs that are in the segment.
115+ while len (visited_pcs ) > 0 and bytecode_offset <= visited_pcs [- 1 ] < segment_end :
116+ visited_pcs .pop ()
117+
118+ return (
119+ BytecodeLeaf (data = bytecode [bytecode_offset :segment_end ]),
120+ bytecode_segment_lengths ,
121+ )
122+
123+ res = []
124+ total_len = 0
125+ for item in bytecode_segment_lengths :
126+ visited_pc_before = visited_pcs [- 1 ] if len (visited_pcs ) > 0 else None
127+
128+ current_structure , item_len = _create_bytecode_segment_structure_inner (
129+ bytecode = bytecode ,
130+ bytecode_segment_lengths = item ,
131+ visited_pcs = visited_pcs ,
132+ bytecode_offset = bytecode_offset ,
133+ )
134+
135+ visited_pc_after = visited_pcs [- 1 ] if len (visited_pcs ) > 0 else None
136+ is_used = visited_pc_after != visited_pc_before
137+
138+ if is_used and visited_pc_before != bytecode_offset :
139+ raise ValueError (
140+ f"Invalid segment structure: PC { visited_pc_before } was visited, "
141+ f"but the beginning of the segment ({ bytecode_offset } ) was not."
142+ )
143+
144+ res .append (
145+ BytecodeSegment (
146+ segment_length = item_len ,
147+ is_used = is_used ,
148+ inner_structure = current_structure ,
149+ )
150+ )
151+ bytecode_offset += item_len
152+ total_len += item_len
153+
154+ return BytecodeSegmentedNode (segments = res ), total_len
0 commit comments