-
Notifications
You must be signed in to change notification settings - Fork 273
/
Copy pathprogram.py
167 lines (144 loc) · 5.81 KB
/
program.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import dataclasses
import string
from abc import ABC, abstractmethod
from dataclasses import field
from typing import Dict, List, Optional, Type, Union
import marshmallow.fields as mfields
import marshmallow_dataclass
from starkware.cairo.lang.compiler.debug_info import DebugInfo
from starkware.cairo.lang.compiler.identifier_definition import (
ConstDefinition,
IdentifierDefinition,
LabelDefinition,
ReferenceDefinition,
)
from starkware.cairo.lang.compiler.identifier_manager import (
IdentifierManager,
MissingIdentifierError,
)
from starkware.cairo.lang.compiler.identifier_manager_field import IdentifierManagerField
from starkware.cairo.lang.compiler.preprocessor.flow import FlowTrackingDataActual, ReferenceManager
from starkware.cairo.lang.compiler.preprocessor.preprocessor import AttributeScope
from starkware.cairo.lang.compiler.references import Reference
from starkware.cairo.lang.compiler.scoped_name import ScopedName, ScopedNameAsStr
from starkware.starkware_utils.marshmallow_dataclass_fields import IntAsHex
from starkware.starkware_utils.serializable_dataclass import SerializableMarshmallowDataclass
@dataclasses.dataclass
class CairoHint:
code: str
accessible_scopes: List[ScopedName] = field(
metadata=dict(marshmallow_field=mfields.List(ScopedNameAsStr))
)
flow_tracking_data: FlowTrackingDataActual
class ProgramBase(ABC):
@abstractmethod
def stripped(self) -> "StrippedProgram":
"""
Returns the program as a StrippedProgram.
"""
prime: int
data: List[int]
builtins: List[str]
main: Optional[int]
@dataclasses.dataclass
class StrippedProgram(ProgramBase):
"""
Cairo program minimal information (stripped from hints, identifiers, etc.). The absence of
hints is crucial for security reasons. Can be used for verifying execution.
"""
prime: int
data: List[int]
builtins: List[str]
main: int
def stripped(self) -> "StrippedProgram":
return self
def run_validity_checks(self):
assert isinstance(self.prime, int) and self.prime > 2 ** 63, "Invalid prime."
assert isinstance(self.data, list) and all(
isinstance(x, int) and 0 <= x < self.prime for x in self.data
), "Invalid program data."
assert (
isinstance(self.builtins, list)
and all(is_valid_builtin_name(builtin) for builtin in self.builtins)
and len(set(self.builtins)) == len(self.builtins)
), "Invalid builtin list."
assert isinstance(self.main, int) and 0 <= self.main < len(
self.data
), "Invalid main() address."
@marshmallow_dataclass.dataclass(repr=False)
class Program(ProgramBase, SerializableMarshmallowDataclass):
prime: int = field(metadata=dict(marshmallow_field=IntAsHex(required=True)))
data: List[int] = field(
metadata=dict(marshmallow_field=mfields.List(IntAsHex(), required=True))
)
hints: Dict[int, List[CairoHint]]
builtins: List[str]
main_scope: ScopedName = field(metadata=dict(marshmallow_field=ScopedNameAsStr()))
identifiers: IdentifierManager = field(
metadata=dict(marshmallow_field=IdentifierManagerField())
)
# Holds all the allocated references in the program.
reference_manager: ReferenceManager
attributes: List[AttributeScope] = field(default_factory=list)
debug_info: Optional[DebugInfo] = None
def stripped(self) -> StrippedProgram:
assert self.main is not None
return StrippedProgram(
prime=self.prime,
data=self.data,
builtins=self.builtins,
main=self.main,
)
def get_identifier(
self,
name: Union[str, ScopedName],
expected_type: Type[IdentifierDefinition],
full_name_lookup: Optional[bool] = None,
):
scoped_name = name if isinstance(name, ScopedName) else ScopedName.from_string(name)
if full_name_lookup is True:
result = self.identifiers.root.get(scoped_name)
else:
result = self.identifiers.search(accessible_scopes=[self.main_scope], name=scoped_name)
result.assert_fully_parsed()
identifier_definition = result.identifier_definition
assert isinstance(identifier_definition, expected_type), (
f"'{scoped_name}' is expected to be {expected_type.TYPE}, "
+ f"found {identifier_definition.TYPE}." # type: ignore
) # type: ignore
return identifier_definition
def get_label(self, name: Union[str, ScopedName], full_name_lookup: Optional[bool] = None):
return self.get_identifier(
name=name, expected_type=LabelDefinition, full_name_lookup=full_name_lookup
).pc
def get_const(self, name: Union[str, ScopedName], full_name_lookup: Optional[bool] = None):
return self.get_identifier(
name=name, expected_type=ConstDefinition, full_name_lookup=full_name_lookup
).value
def get_reference_binds(self, name: Union[str, ScopedName]) -> List[Reference]:
"""
Returns all the references associated with the given name. Returns more than one value if
the reference was rebound.
"""
return self.get_identifier(name, ReferenceDefinition).references
@property
def main(self) -> Optional[int]: # type: ignore
try:
return self.get_label("main")
except MissingIdentifierError:
return None
@property
def start(self) -> int:
try:
return self.get_label("__start__")
except MissingIdentifierError:
return 0
def is_valid_builtin_name(name: str) -> bool:
"""
Returns true if name may be used as a builtin name.
"""
return (
isinstance(name, str)
and len(name) < 1000
and set(name) <= {*string.ascii_lowercase, *string.digits, "_"}
)